Skip to content

Commit

Permalink
Add DigitalOcean datasource digitalocean_image
Browse files Browse the repository at this point in the history
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 committed Apr 20, 2017
1 parent 3e8dca1 commit 54da4d3
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 0 deletions.
93 changes: 93 additions & 0 deletions builtin/providers/digitalocean/datasource_digitaloceal_image.go
Original file line number Diff line number Diff line change
@@ -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))
}
110 changes: 110 additions & 0 deletions builtin/providers/digitalocean/datasource_digitaloceal_image_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package digitalocean

import (
"fmt"
"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
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),
takeSnapshotOfDroplet(&droplet, rInt),
),
Destroy: false,
},
{
Config: testAccCheckDigitalOceanImageConfig_basic(rInt),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"data.digitalocean_image.foobar", "name", fmt.Sprintf("snap-%d", 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"),
),
Destroy: false,
},
{
Config: testAccCheckDigitalOceanImageConfig_basic(rInt),
Check: resource.ComposeTestCheckFunc(
takeSnapshotOfDroplet(&droplet, rInt),
),
Destroy: false,
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: testAccCheckDigitalOceanDropletConfig_basic(rInt),
Check: resource.ComposeTestCheckFunc(
deleteSnapshotsOfDroplet(&droplet),
),
},
},
})
}

func takeSnapshotOfDroplet(droplet *godo.Droplet, rInt int) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*godo.Client)
action, _, err := client.DropletActions.Snapshot((*droplet).ID, fmt.Sprintf("snap-%d", rInt))
if err != nil {
return err
}
waitForAction(client, action)
return nil
}
}

func deleteSnapshotsOfDroplet(droplet *godo.Droplet) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*godo.Client)
snapshots := (*droplet).SnapshotIDs
for _, value := range snapshots {
_, err := client.Images.Delete(value)
if err != nil {
return err
}
}
return nil
}
}

func testAccCheckDigitalOceanImageConfig_basic(rInt int) string {
return fmt.Sprintf(`
%s
data "digitalocean_image" "foobar" {
name = "snap-%d"
}
`, testAccCheckDigitalOceanDropletConfig_basic(rInt), rInt)
}

func testAccCheckDigitalOceanImageConfig_nonexisting(rInt int) string {
return fmt.Sprintf(`
%s
data "digitalocean_image" "foobar" {
name = "snap-%d-nonexisting"
}
`, testAccCheckDigitalOceanDropletConfig_basic(rInt), rInt)
}
4 changes: 4 additions & 0 deletions builtin/providers/digitalocean/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
46 changes: 46 additions & 0 deletions website/source/docs/providers/do/d/image.html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
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

Option 1: From a name:

Reference the name:

```hcl
data "digitalocean_image" "example1" {
name = "example-1.0.0"
}
```

## 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 54da4d3

Please sign in to comment.