Permalink
Browse files

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>
  • Loading branch information...
roidelapluie authored and stack72 committed Apr 21, 2017
1 parent be0390e commit c2a1e688cb19aef1ac76d7f360876824ce4f8c46
@@ -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".

0 comments on commit c2a1e68

Please sign in to comment.