Add DigitalOcean datasource digitalocean_image (#13787)
Add a new data source for Digital Ocean that finds snapshots by name. Signed-off-by: Julien Pivotto <roidelapluie@inuits.eu>
This commit is contained in:
parent
be0390e561
commit
c2a1e688cb
|
@ -0,0 +1,93 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourceDigitalOceanImage() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceDigitalOceanImageRead,
|
||||
Schema: map[string]*schema.Schema{
|
||||
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "name of the image",
|
||||
},
|
||||
// computed attributes
|
||||
"image": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "slug or id of the image",
|
||||
},
|
||||
"min_disk_size": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
Description: "minimum disk size required by the image",
|
||||
},
|
||||
"private": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Computed: true,
|
||||
Description: "Is the image private or non-private",
|
||||
},
|
||||
"regions": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Description: "list of the regions that the image is available in",
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
"type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "type of the image",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceDigitalOceanImageRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*godo.Client)
|
||||
|
||||
opts := &godo.ListOptions{}
|
||||
|
||||
images, _, err := client.Images.ListUser(opts)
|
||||
if err != nil {
|
||||
d.SetId("")
|
||||
return err
|
||||
}
|
||||
image, err := findImageByName(images, d.Get("name").(string))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetId(image.Name)
|
||||
d.Set("name", image.Name)
|
||||
d.Set("image", strconv.Itoa(image.ID))
|
||||
d.Set("min_disk_size", image.MinDiskSize)
|
||||
d.Set("private", !image.Public)
|
||||
d.Set("regions", image.Regions)
|
||||
d.Set("type", image.Type)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findImageByName(images []godo.Image, name string) (*godo.Image, error) {
|
||||
results := make([]godo.Image, 0)
|
||||
for _, v := range images {
|
||||
if v.Name == name {
|
||||
results = append(results, v)
|
||||
}
|
||||
}
|
||||
if len(results) == 1 {
|
||||
return &results[0], nil
|
||||
}
|
||||
if len(results) == 0 {
|
||||
return nil, fmt.Errorf("no user image found with name %s", name)
|
||||
}
|
||||
return nil, fmt.Errorf("too many user images found with name %s (found %d, expected 1)", name, len(results))
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDigitalOceanImage_Basic(t *testing.T) {
|
||||
var droplet godo.Droplet
|
||||
var snapshotsId []int
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDigitalOceanDropletDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCheckDigitalOceanDropletConfig_basic(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet),
|
||||
takeSnapshotsOfDroplet(rInt, &droplet, &snapshotsId),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccCheckDigitalOceanImageConfig_basic(rInt, 1),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_image.foobar", "name", fmt.Sprintf("snap-%d-1", rInt)),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_image.foobar", "min_disk_size", "20"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_image.foobar", "private", "true"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_image.foobar", "type", "snapshot"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccCheckDigitalOceanImageConfig_basic(rInt, 0),
|
||||
ExpectError: regexp.MustCompile(`.*too many user images found with name snap-.*\ .found 2, expected 1.`),
|
||||
},
|
||||
{
|
||||
Config: testAccCheckDigitalOceanImageConfig_nonexisting(rInt),
|
||||
Destroy: false,
|
||||
ExpectError: regexp.MustCompile(`.*no user image found with name snap-.*-nonexisting`),
|
||||
},
|
||||
{
|
||||
Config: " ",
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
deleteSnapshots(&snapshotsId),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func takeSnapshotsOfDroplet(rInt int, droplet *godo.Droplet, snapshotsId *[]int) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*godo.Client)
|
||||
for i := 0; i < 3; i++ {
|
||||
err := takeSnapshotOfDroplet(rInt, i%2, droplet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
retrieveDroplet, _, err := client.Droplets.Get((*droplet).ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*snapshotsId = retrieveDroplet.SnapshotIDs
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func takeSnapshotOfDroplet(rInt, sInt int, droplet *godo.Droplet) error {
|
||||
client := testAccProvider.Meta().(*godo.Client)
|
||||
action, _, err := client.DropletActions.Snapshot((*droplet).ID, fmt.Sprintf("snap-%d-%d", rInt, sInt))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
waitForAction(client, action)
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteSnapshots(snapshotsId *[]int) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
log.Printf("XXX Deleting snaps")
|
||||
client := testAccProvider.Meta().(*godo.Client)
|
||||
snapshots := *snapshotsId
|
||||
for _, value := range snapshots {
|
||||
log.Printf("XXX Deleting %d", value)
|
||||
_, err := client.Images.Delete(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanImageConfig_basic(rInt, sInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
data "digitalocean_image" "foobar" {
|
||||
name = "snap-%d-%d"
|
||||
}
|
||||
`, rInt, sInt)
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanImageConfig_nonexisting(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
data "digitalocean_image" "foobar" {
|
||||
name = "snap-%d-nonexisting"
|
||||
}
|
||||
`, rInt)
|
||||
}
|
|
@ -17,6 +17,10 @@ func Provider() terraform.ResourceProvider {
|
|||
},
|
||||
},
|
||||
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"digitalocean_image": dataSourceDigitalOceanImage(),
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"digitalocean_domain": resourceDigitalOceanDomain(),
|
||||
"digitalocean_droplet": resourceDigitalOceanDroplet(),
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
layout: "digitalocean"
|
||||
page_title: "DigitalOcean: digitalocean_image"
|
||||
sidebar_current: "docs-do-datasource-image"
|
||||
description: |-
|
||||
Get information on an snapshot.
|
||||
---
|
||||
|
||||
# digitalocean_image
|
||||
|
||||
Get information on an snapshot images. The aim of this datasource is to enable
|
||||
you to build droplets based on snapshot names.
|
||||
|
||||
An error is triggered if zero or more than one result is returned by the query.
|
||||
|
||||
## Example Usage
|
||||
|
||||
Get the data about a snapshot:
|
||||
|
||||
```hcl
|
||||
data "digitalocean_image" "example1" {
|
||||
name = "example-1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
Reuse the data about a snapshot to create a droplet:
|
||||
|
||||
```hcl
|
||||
data "digitalocean_image" "example1" {
|
||||
name = "example-1.0.0"
|
||||
}
|
||||
resource "digitalocean_droplet" "example1" {
|
||||
image = "${data.digitalocean_image.example1.image}"
|
||||
name = "example-1"
|
||||
region = "nyc2"
|
||||
size = "512mb"
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `name` - The name of the image.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `name` - See Argument Reference above.
|
||||
* `image` - The id of the image.
|
||||
* `min_disk_size`: The minimum 'disk' required for the image.
|
||||
* `private` - Is image a public image or not. Public images represents
|
||||
Linux distributions or Application, while non-public images represent
|
||||
snapshots and backups and are only available within your account.
|
||||
* `regions`: The regions that the image is available in.
|
||||
* `size_gigabytes`: The size of the image in gigabytes.
|
||||
* `type`: Type of the image. Can be "snapshot" or "backup".
|
Loading…
Reference in New Issue