add digitalocean_droplets data source (#418)

* add digitalocean_droplets data source

* add docs

* Update website/docs/d/droplets.html.md

Co-Authored-By: Andrew Starr-Bochicchio <andrewsomething@users.noreply.github.com>

* Update website/docs/d/droplets.html.md

Co-Authored-By: Andrew Starr-Bochicchio <andrewsomething@users.noreply.github.com>

* Update digitalocean/droplets.go

Co-Authored-By: Andrew Starr-Bochicchio <andrewsomething@users.noreply.github.com>

* Import strconv

Co-authored-by: Andrew Starr-Bochicchio <andrewsomething@users.noreply.github.com>
Co-authored-by: Andrew Starr-Bochicchio <a.starr.b@gmail.com>
This commit is contained in:
Tom Dyas 2020-04-22 14:09:18 -07:00 committed by GitHub
parent 0fea588f83
commit 11fd5a433a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 505 additions and 239 deletions

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"strconv"
"strings"
"github.com/digitalocean/godo"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
@ -12,217 +11,91 @@ import (
)
func dataSourceDigitalOceanDroplet() *schema.Resource {
recordSchema := dropletSchema()
for _, f := range recordSchema {
f.Computed = true
}
recordSchema["id"].ExactlyOneOf = []string{"id", "tag", "name"}
recordSchema["id"].Optional = true
recordSchema["name"].ExactlyOneOf = []string{"id", "tag", "name"}
recordSchema["name"].Optional = true
recordSchema["tag"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "unique tag of the Droplet",
ValidateFunc: validation.NoZeroValues,
ExactlyOneOf: []string{"id", "tag", "name"},
}
return &schema.Resource{
Read: dataSourceDigitalOceanDropletRead,
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeInt,
Optional: true,
Description: "id of the Droplet",
ValidateFunc: validation.NoZeroValues,
ExactlyOneOf: []string{"id", "tag", "name"},
},
"tag": {
Type: schema.TypeString,
Optional: true,
Description: "unique tag of the Droplet",
ValidateFunc: validation.NoZeroValues,
ExactlyOneOf: []string{"id", "tag", "name"},
},
"name": {
Type: schema.TypeString,
Optional: true,
Description: "name of the Droplet",
ValidateFunc: validation.NoZeroValues,
ExactlyOneOf: []string{"id", "tag", "name"},
},
// computed attributes
"created_at": {
Type: schema.TypeString,
Computed: true,
Description: "the creation date for the Droplet",
},
"urn": {
Type: schema.TypeString,
Computed: true,
Description: "the uniform resource name for the Droplet",
},
"region": {
Type: schema.TypeString,
Computed: true,
Description: "the region that the Droplet instance is deployed in",
},
"image": {
Type: schema.TypeString,
Computed: true,
Description: "the image id or slug of the Droplet",
},
"size": {
Type: schema.TypeString,
Computed: true,
Description: "the current size of the Droplet",
},
"disk": {
Type: schema.TypeInt,
Computed: true,
Description: "the size of the Droplets disk in gigabytes",
},
"vcpus": {
Type: schema.TypeInt,
Computed: true,
Description: "the number of virtual cpus",
},
"memory": {
Type: schema.TypeInt,
Computed: true,
Description: "memory of the Droplet in megabytes",
},
"price_hourly": {
Type: schema.TypeFloat,
Computed: true,
Description: "the Droplets hourly price",
},
"price_monthly": {
Type: schema.TypeFloat,
Computed: true,
Description: "the Droplets monthly price",
},
"status": {
Type: schema.TypeString,
Computed: true,
Description: "state of the Droplet instance",
},
"locked": {
Type: schema.TypeBool,
Computed: true,
Description: "whether the Droplet has been locked",
},
"ipv4_address": {
Type: schema.TypeString,
Computed: true,
Description: "the Droplets public ipv4 address",
},
"ipv4_address_private": {
Type: schema.TypeString,
Computed: true,
Description: "the Droplets private ipv4 address",
},
"ipv6_address": {
Type: schema.TypeString,
Computed: true,
Description: "the Droplets public ipv6 address",
},
"ipv6_address_private": {
Type: schema.TypeString,
Computed: true,
Description: "the Droplets private ipv4 address",
},
"backups": {
Type: schema.TypeBool,
Computed: true,
Description: "whether the Droplet has backups enabled",
},
"ipv6": {
Type: schema.TypeBool,
Computed: true,
Description: "whether the Droplet has ipv6 enabled",
},
"private_networking": {
Type: schema.TypeBool,
Computed: true,
Description: "whether the Droplet has private networking enabled",
},
"monitoring": {
Type: schema.TypeBool,
Computed: true,
Description: "whether the Droplet has monitoring enabled",
},
"volume_ids": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
Description: "list of volumes attached to the Droplet",
},
"tags": tagsDataSourceSchema(),
"vpc_uuid": {
Type: schema.TypeString,
Computed: true,
Description: "UUID of the VPC in which the Droplet is located",
},
},
Read: dataSourceDigitalOceanDropletRead,
Schema: recordSchema,
}
}
func dataSourceDigitalOceanDropletRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*CombinedConfig).godoClient()
var foundDroplet godo.Droplet
if id, ok := d.GetOk("id"); ok {
droplet, _, err := client.Droplets.Get(context.Background(), id.(int))
if err != nil {
return err
}
exportDropletProperties(d, droplet)
return nil
}
opts := &godo.ListOptions{
Page: 1,
PerPage: 200,
}
dropletList := []godo.Droplet{}
for {
droplets, resp, err := client.Droplets.List(context.Background(), opts)
if err != nil {
return fmt.Errorf("Error retrieving droplets: %s", err)
}
for _, droplet := range droplets {
dropletList = append(dropletList, droplet)
}
if resp.Links == nil || resp.Links.IsLastPage() {
break
}
page, err := resp.Links.CurrentPage()
if err != nil {
return fmt.Errorf("Error retrieving droplets: %s", err)
}
opts.Page = page + 1
}
if v, ok := d.GetOk("tag"); ok {
droplet, err := findDropletByTag(dropletList, v.(string))
foundDroplet = *droplet
} else if v, ok := d.GetOk("tag"); ok {
dropletList, err := getDigitalOceanDroplets(meta)
if err != nil {
return err
}
exportDropletProperties(d, droplet)
droplet, err := findDropletByTag(dropletList, v.(string))
if err != nil {
return err
}
foundDroplet = *droplet
} else if v, ok := d.GetOk("name"); ok {
dropletList, err := getDigitalOceanDroplets(meta)
if err != nil {
return err
}
droplet, err := findDropletByName(dropletList, v.(string))
if err != nil {
return err
}
exportDropletProperties(d, droplet)
foundDroplet = *droplet
} else {
return fmt.Errorf("Error: specify either a name, tag, or id to use to look up the droplet")
}
flattenedDroplet, err := flattenDigitalOceanDroplet(foundDroplet, meta)
if err != nil {
return err
}
if err := setResourceDataFromMap(d, flattenedDroplet); err != nil {
return err
}
d.SetId(strconv.Itoa(foundDroplet.ID))
return nil
}
func findDropletByName(droplets []godo.Droplet, name string) (*godo.Droplet, error) {
func findDropletByName(droplets []interface{}, name string) (*godo.Droplet, error) {
results := make([]godo.Droplet, 0)
for _, v := range droplets {
if v.Name == name {
results = append(results, v)
droplet := v.(godo.Droplet)
if droplet.Name == name {
results = append(results, droplet)
}
}
if len(results) == 1 {
@ -234,12 +107,13 @@ func findDropletByName(droplets []godo.Droplet, name string) (*godo.Droplet, err
return nil, fmt.Errorf("too many droplets found with name %s (found %d, expected 1)", name, len(results))
}
func findDropletByTag(droplets []godo.Droplet, tag string) (*godo.Droplet, error) {
func findDropletByTag(droplets []interface{}, tag string) (*godo.Droplet, error) {
results := make([]godo.Droplet, 0)
for _, d := range droplets {
for _, t := range d.Tags {
droplet := d.(godo.Droplet)
for _, t := range droplet.Tags {
if t == tag {
results = append(results, d)
results = append(results, droplet)
}
}
}
@ -251,59 +125,3 @@ func findDropletByTag(droplets []godo.Droplet, tag string) (*godo.Droplet, error
}
return nil, fmt.Errorf("too many droplets found with tag %s (found %d, expected 1)", tag, len(results))
}
func exportDropletProperties(d *schema.ResourceData, droplet *godo.Droplet) error {
d.SetId(strconv.Itoa(droplet.ID))
d.Set("name", droplet.Name)
d.Set("urn", droplet.URN())
d.Set("region", droplet.Region.Slug)
d.Set("size", droplet.Size.Slug)
d.Set("price_hourly", droplet.Size.PriceHourly)
d.Set("price_monthly", droplet.Size.PriceMonthly)
d.Set("disk", droplet.Disk)
d.Set("vcpus", droplet.Vcpus)
d.Set("memory", droplet.Memory)
d.Set("status", droplet.Status)
d.Set("locked", droplet.Locked)
d.Set("created_at", droplet.Created)
d.Set("vpc_uuid", droplet.VPCUUID)
if droplet.Image.Slug == "" {
d.Set("image", droplet.Image.ID)
} else {
d.Set("image", droplet.Image.Slug)
}
if publicIPv4 := findIPv4AddrByType(droplet, "public"); publicIPv4 != "" {
d.Set("ipv4_address", publicIPv4)
}
if privateIPv4 := findIPv4AddrByType(droplet, "private"); privateIPv4 != "" {
d.Set("ipv4_address_private", privateIPv4)
}
if publicIPv6 := findIPv6AddrByType(droplet, "public"); publicIPv6 != "" {
d.Set("ipv6_address", strings.ToLower(publicIPv6))
}
if privateIPv6 := findIPv6AddrByType(droplet, "private"); privateIPv6 != "" {
d.Set("ipv6_address_private", strings.ToLower(privateIPv6))
}
if features := droplet.Features; features != nil {
d.Set("backups", containsDigitalOceanDropletFeature(features, "backups"))
d.Set("ipv6", containsDigitalOceanDropletFeature(features, "ipv6"))
d.Set("private_networking", containsDigitalOceanDropletFeature(features, "private_networking"))
d.Set("monitoring", containsDigitalOceanDropletFeature(features, "monitoring"))
}
if err := d.Set("volume_ids", flattenDigitalOceanDropletVolumeIds(droplet.VolumeIDs)); err != nil {
return fmt.Errorf("Error setting `volume_ids`: %+v", err)
}
if err := d.Set("tags", flattenTags(droplet.Tags)); err != nil {
return fmt.Errorf("Error setting `tags`: %+v", err)
}
return nil
}

View File

@ -0,0 +1,69 @@
package digitalocean
import (
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/terraform-providers/terraform-provider-digitalocean/internal/datalist"
)
func dataSourceDigitalOceanDroplets() *schema.Resource {
dataListConfig := &datalist.ResourceConfig{
RecordSchema: dropletSchema(),
FilterKeys: []string{
"id",
"name",
"created_at",
"urn",
"region",
"image",
"size",
"disk",
"vcpus",
"memory",
"price_hourly",
"price_monthly",
"status",
"locked",
"ipv4_address",
"ipv4_address_private",
"ipv6_address",
"ipv6_address_private",
"backups",
"ipv6",
"private_networking",
"monitoring",
"volume_ids",
"tags",
"vpc_uuid",
},
SortKeys: []string{
"id",
"name",
"created_at",
"urn",
"region",
"image",
"size",
"disk",
"vcpus",
"memory",
"price_hourly",
"price_monthly",
"status",
"locked",
"ipv4_address",
"ipv4_address_private",
"ipv6_address",
"ipv6_address_private",
"backups",
"ipv6",
"private_networking",
"monitoring",
"vpc_uuid",
},
ResultAttributeName: "droplets",
GetRecords: getDigitalOceanDroplets,
FlattenRecord: flattenDigitalOceanDroplet,
}
return datalist.NewResource(dataListConfig)
}

View File

@ -0,0 +1,58 @@
package digitalocean
import (
"fmt"
"testing"
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)
func TestAccDataSourceDigitalOceanDroplets_Basic(t *testing.T) {
name1 := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(10))
name2 := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(10))
resourcesConfig := fmt.Sprintf(`
resource "digitalocean_droplet" "foo" {
name = "%s"
size = "s-1vcpu-1gb"
image = "centos-7-x64"
region = "nyc3"
}
resource "digitalocean_droplet" "bar" {
name = "%s"
size = "s-1vcpu-1gb"
image = "centos-7-x64"
region = "nyc3"
}
`, name1, name2)
datasourceConfig := fmt.Sprintf(`
data "digitalocean_droplets" "result" {
filter {
key = "name"
values = ["%s"]
}
}
`, name1)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: resourcesConfig,
},
{
Config: resourcesConfig + datasourceConfig,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.digitalocean_droplets.result", "droplets.#", "1"),
resource.TestCheckResourceAttr("data.digitalocean_droplets.result", "droplets.0.name", name1),
),
},
{
Config: resourcesConfig,
},
},
})
}

205
digitalocean/droplets.go Normal file
View File

@ -0,0 +1,205 @@
package digitalocean
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/digitalocean/godo"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
func dropletSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"id": {
Type: schema.TypeInt,
Description: "id of the Droplet",
},
"name": {
Type: schema.TypeString,
Description: "name of the Droplet",
},
"created_at": {
Type: schema.TypeString,
Description: "the creation date for the Droplet",
},
"urn": {
Type: schema.TypeString,
Description: "the uniform resource name for the Droplet",
},
"region": {
Type: schema.TypeString,
Description: "the region that the Droplet instance is deployed in",
},
"image": {
Type: schema.TypeString,
Description: "the image id or slug of the Droplet",
},
"size": {
Type: schema.TypeString,
Description: "the current size of the Droplet",
},
"disk": {
Type: schema.TypeInt,
Description: "the size of the Droplets disk in gigabytes",
},
"vcpus": {
Type: schema.TypeInt,
Description: "the number of virtual cpus",
},
"memory": {
Type: schema.TypeInt,
Description: "memory of the Droplet in megabytes",
},
"price_hourly": {
Type: schema.TypeFloat,
Description: "the Droplets hourly price",
},
"price_monthly": {
Type: schema.TypeFloat,
Description: "the Droplets monthly price",
},
"status": {
Type: schema.TypeString,
Description: "state of the Droplet instance",
},
"locked": {
Type: schema.TypeBool,
Description: "whether the Droplet has been locked",
},
"ipv4_address": {
Type: schema.TypeString,
Description: "the Droplets public ipv4 address",
},
"ipv4_address_private": {
Type: schema.TypeString,
Description: "the Droplets private ipv4 address",
},
"ipv6_address": {
Type: schema.TypeString,
Description: "the Droplets public ipv6 address",
},
"ipv6_address_private": {
Type: schema.TypeString,
Description: "the Droplets private ipv4 address",
},
"backups": {
Type: schema.TypeBool,
Description: "whether the Droplet has backups enabled",
},
"ipv6": {
Type: schema.TypeBool,
Description: "whether the Droplet has ipv6 enabled",
},
"private_networking": {
Type: schema.TypeBool,
Description: "whether the Droplet has private networking enabled",
},
"monitoring": {
Type: schema.TypeBool,
Description: "whether the Droplet has monitoring enabled",
},
"volume_ids": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Description: "list of volumes attached to the Droplet",
},
"tags": tagsDataSourceSchema(),
"vpc_uuid": {
Type: schema.TypeString,
Description: "UUID of the VPC in which the Droplet is located",
},
}
}
func getDigitalOceanDroplets(meta interface{}) ([]interface{}, error) {
client := meta.(*CombinedConfig).godoClient()
opts := &godo.ListOptions{
Page: 1,
PerPage: 200,
}
var dropletList []interface{}
for {
droplets, resp, err := client.Droplets.List(context.Background(), opts)
if err != nil {
return nil, fmt.Errorf("Error retrieving droplets: %s", err)
}
for _, droplet := range droplets {
dropletList = append(dropletList, droplet)
}
if resp.Links == nil || resp.Links.IsLastPage() {
break
}
page, err := resp.Links.CurrentPage()
if err != nil {
return nil, fmt.Errorf("Error retrieving droplets: %s", err)
}
opts.Page = page + 1
}
return dropletList, nil
}
func flattenDigitalOceanDroplet(rawDroplet, meta interface{}) (map[string]interface{}, error) {
droplet := rawDroplet.(godo.Droplet)
flattenedDroplet := map[string]interface{}{
"name": droplet.Name,
"urn": droplet.URN(),
"region": droplet.Region.Slug,
"size": droplet.Size.Slug,
"price_hourly": droplet.Size.PriceHourly,
"price_monthly": droplet.Size.PriceMonthly,
"disk": droplet.Disk,
"vcpus": droplet.Vcpus,
"memory": droplet.Memory,
"status": droplet.Status,
"locked": droplet.Locked,
"created_at": droplet.Created,
"vpc_uuid": droplet.VPCUUID,
}
if droplet.Image.Slug == "" {
flattenedDroplet["image"] = strconv.Itoa(droplet.Image.ID)
} else {
flattenedDroplet["image"] = droplet.Image.Slug
}
if publicIPv4 := findIPv4AddrByType(&droplet, "public"); publicIPv4 != "" {
flattenedDroplet["ipv4_address"] = publicIPv4
}
if privateIPv4 := findIPv4AddrByType(&droplet, "private"); privateIPv4 != "" {
flattenedDroplet["ipv4_address_private"] = privateIPv4
}
if publicIPv6 := findIPv6AddrByType(&droplet, "public"); publicIPv6 != "" {
flattenedDroplet["ipv6_address"] = strings.ToLower(publicIPv6)
}
if privateIPv6 := findIPv6AddrByType(&droplet, "private"); privateIPv6 != "" {
flattenedDroplet["ipv6_address_private"] = strings.ToLower(privateIPv6)
}
if features := droplet.Features; features != nil {
flattenedDroplet["backups"] = containsDigitalOceanDropletFeature(features, "backups")
flattenedDroplet["ipv6"] = containsDigitalOceanDropletFeature(features, "ipv6")
flattenedDroplet["private_networking"] = containsDigitalOceanDropletFeature(features, "private_networking")
flattenedDroplet["monitoring"] = containsDigitalOceanDropletFeature(features, "monitoring")
}
flattenedDroplet["volume_ids"] = flattenDigitalOceanDropletVolumeIds(droplet.VolumeIDs)
flattenedDroplet["tags"] = flattenTags(droplet.Tags)
return flattenedDroplet, nil
}

View File

@ -50,6 +50,7 @@ func Provider() terraform.ResourceProvider {
"digitalocean_database_cluster": dataSourceDigitalOceanDatabaseCluster(),
"digitalocean_domain": dataSourceDigitalOceanDomain(),
"digitalocean_droplet": dataSourceDigitalOceanDroplet(),
"digitalocean_droplets": dataSourceDigitalOceanDroplets(),
"digitalocean_droplet_snapshot": dataSourceDigitalOceanDropletSnapshot(),
"digitalocean_floating_ip": dataSourceDigitalOceanFloatingIp(),
"digitalocean_image": dataSourceDigitalOceanImage(),

View File

@ -111,6 +111,11 @@ func dataListResourceRead(config *ResourceConfig) schema.ReadFunc {
// Validate a ResourceConfig to ensure it conforms to this package's assumptions.
func validateResourceConfig(config *ResourceConfig) error {
// Ensure that ResultAttributeName exists.
if config.ResultAttributeName == "" {
return fmt.Errorf("ResultAttributeName must be specified")
}
// Ensure that all of the filter keys exist in the schema and are of a supported type.
for _, filterKey := range config.FilterKeys {
if s, ok := config.RecordSchema[filterKey]; ok {

View File

@ -28,6 +28,9 @@
<li<%= sidebar_current("docs-do-datasource-droplet") %>>
<a href="/docs/providers/do/d/droplet.html">digitalocean_droplet</a>
</li>
<li<%= sidebar_current("docs-do-datasource-droplets") %>>
<a href="/docs/providers/do/d/droplets.html">digitalocean_droplets</a>
</li>
<li<%= sidebar_current("docs-do-datasource-droplet-snapshot") %>>
<a href="/docs/providers/do/d/droplet_snapshot.html">digitalocean_droplet_snapshot</a>
</li>

View File

@ -0,0 +1,107 @@
---
layout: "digitalocean"
page_title: "DigitalOcean: digitalocean_droplets"
sidebar_current: "docs-do-datasource-droplets"
description: |-
Retrieve information on Droplets.
---
# digitalocean_droplets
Get information on Droplets for use in other resources, with the ability to filter and sort the results.
If no filters are specified, all Droplets will be returned.
This data source is useful if the Droplets in question are not managed by Terraform or you need to
utilize any of the Droplets' data.
Note: You can use the [`digitalocean_droplet`](/docs/providers/do/d/droplet.html) data source to obtain metadata
about a single Droplet if you already know the `id`, unique `name`, or unique `tag` to retrieve.
## Example Usage
Use the `filter` block with a `key` string and `values` list to filter images.
For example to find all Droplets with size `s-1vcpu-1gb`:
```hcl
data "digitalocean_droplets" "small" {
filter {
key = "size"
values = ["s-1vcpu-1gb"]
}
}
```
You can filter on multiple fields and sort the results as well:
```hcl
data "digitalocean_droplets" "small-with-backups" {
filter {
key = "size"
values = ["s-1vcpu-1gb"]
}
filter {
key = "backups"
values = ["true"]
}
sort {
key = "created_at"
direction = "desc"
}
}
```
## Argument Reference
* `filter` - (Optional) Filter the results.
The `filter` block is documented below.
* `sort` - (Optional) Sort the results.
The `sort` block is documented below.
`filter` supports the following arguments:
* `key` - (Required) Filter the Droplets by this key. This may be one of '`backups`, `created_at`, `disk`, `id`,
`image`, `ipv4_address`, `ipv4_address_private`, `ipv6`, `ipv6_address`, `ipv6_address_private`, `locked`,
`memory`, `monitoring`, `name`, `price_hourly`, `price_monthly`, `private_networking`, `region`, `size`,
`status`, `tags`, `urn`, `vcpus`, `volume_ids`, or `vpc_uuid`'.
* `values` - (Required) A list of values to match against the `key` field. Only retrieves Droplets
where the `key` field takes on one or more of the values provided here.
`sort` supports the following arguments:
* `key` - (Required) Sort the Droplets by this key. This may be one of `backups`, `created_at`, `disk`, `id`,
`image`, `ipv4_address`, `ipv4_address_private`, `ipv6`, `ipv6_address`, `ipv6_address_private`, `locked`,
`memory`, `monitoring`, `name`, `price_hourly`, `price_monthly`, `private_networking`, `region`, `size`,
`status`, `urn`, `vcpus`, or `vpc_uuid`.
* `direction` - (Required) The sort direction. This may be either `asc` or `desc`.
## Attributes Reference
* `droplets` - A list of Droplets satisfying any `filter` and `sort` criteria. Each Droplet has the following attributes:
- `id` - The ID of the Droplet.
- `urn` - The uniform resource name of the Droplet
- `region` - The region the Droplet is running in.
- `image` - The Droplet image ID or slug.
- `size` - The unique slug that identifies the type of Droplet.
- `disk` - The size of the Droplet's disk in GB.
- `vcpus` - The number of the Droplet's virtual CPUs.
- `memory` - The amount of the Droplet's memory in MB.
- `price_hourly` - Droplet hourly price.
- `price_monthly` - Droplet monthly price.
- `status` - The status of the Droplet.
- `locked` - Whether the Droplet is locked.
- `ipv6_address` - The Droplet's public IPv6 address
- `ipv6_address_private` - The Droplet's private IPv6 address
- `ipv4_address` - The Droplet's public IPv4 address
- `ipv4_address_private` - The Droplet's private IPv4 address
- `backups` - Whether backups are enabled.
- `ipv6` - Whether IPv6 is enabled.
- `private_networking` - Whether private networks are enabled.
- `monitoring` - Whether monitoring agent is installed.
- `volume_ids` - List of the IDs of each volumes attached to the Droplet.
- `tags` - A list of the tags associated to the Droplet.
- `vpc_uuid` - The ID of the VPC where the Droplet is located.