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:
Julien Pivotto 2017-04-21 15:25:34 +02:00 committed by Paul Stack
parent be0390e561
commit c2a1e688cb
4 changed files with 277 additions and 0 deletions

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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(),

View File

@ -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".