provider/openstack: Image Data Source (#12097)
* vendor: Updating Gophercloud * provider/openstack: Image Data Source This commit adds the openstack_images_image_v2 data source which is able to query the Image Service v2 API for a specific image.
This commit is contained in:
parent
20b312034a
commit
8ea8588c52
|
@ -0,0 +1,255 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourceImagesImageV2() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceImagesImageV2Read,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"region": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
|
||||
},
|
||||
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"visibility": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"owner": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"size_min": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"size_max": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"sort_key": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: "name",
|
||||
},
|
||||
|
||||
"sort_direction": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: "asc",
|
||||
ValidateFunc: dataSourceImagesImageV2SortDirection,
|
||||
},
|
||||
|
||||
"tag": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"most_recent": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
// Computed values
|
||||
"container_format": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"disk_format": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"min_disk_gb": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"min_ram_mb": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"protected": {
|
||||
Type: schema.TypeBool,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"checksum": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"size_bytes": {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"metadata": {
|
||||
Type: schema.TypeMap,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"updated_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"file": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"schema": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// dataSourceImagesImageV2Read performs the image lookup.
|
||||
func dataSourceImagesImageV2Read(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
imageClient, err := config.imageV2Client(GetRegion(d))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating OpenStack image client: %s", err)
|
||||
}
|
||||
|
||||
visibility := resourceImagesImageV2VisibilityFromString(d.Get("visibility").(string))
|
||||
|
||||
listOpts := images.ListOpts{
|
||||
Name: d.Get("name").(string),
|
||||
Visibility: visibility,
|
||||
Owner: d.Get("owner").(string),
|
||||
Status: images.ImageStatusActive,
|
||||
SizeMin: int64(d.Get("size_min").(int)),
|
||||
SizeMax: int64(d.Get("size_max").(int)),
|
||||
SortKey: d.Get("sort_key").(string),
|
||||
SortDir: d.Get("sort_direction").(string),
|
||||
Tag: d.Get("tag").(string),
|
||||
}
|
||||
|
||||
var allImages []images.Image
|
||||
pager := images.List(imageClient, listOpts)
|
||||
err = pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
images, err := images.ExtractImages(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, i := range images {
|
||||
allImages = append(allImages, i)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to retrieve images: %s", err)
|
||||
}
|
||||
|
||||
var image images.Image
|
||||
if len(allImages) < 1 {
|
||||
return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.")
|
||||
}
|
||||
|
||||
if len(allImages) > 1 {
|
||||
recent := d.Get("most_recent").(bool)
|
||||
log.Printf("[DEBUG] openstack_images_image: multiple results found and `most_recent` is set to: %t", recent)
|
||||
if recent {
|
||||
image = mostRecentImage(allImages)
|
||||
} else {
|
||||
return fmt.Errorf("Your query returned more than one result. Please try a more " +
|
||||
"specific search criteria, or set `most_recent` attribute to true.")
|
||||
}
|
||||
} else {
|
||||
image = allImages[0]
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] openstack_images_image: Single Image found: %s", image.ID)
|
||||
return dataSourceImagesImageV2Attributes(d, &image)
|
||||
}
|
||||
|
||||
// dataSourceImagesImageV2Attributes populates the fields of an Image resource.
|
||||
func dataSourceImagesImageV2Attributes(d *schema.ResourceData, image *images.Image) error {
|
||||
log.Printf("[DEBUG] openstack_images_image details: %#v", image)
|
||||
|
||||
d.SetId(image.ID)
|
||||
d.Set("name", image.Name)
|
||||
d.Set("tags", image.Tags)
|
||||
d.Set("container_format", image.ContainerFormat)
|
||||
d.Set("disk_format", image.DiskFormat)
|
||||
d.Set("min_disk_gb", image.MinDiskGigabytes)
|
||||
d.Set("min_ram_mb", image.MinRAMMegabytes)
|
||||
d.Set("owner", image.Owner)
|
||||
d.Set("protected", image.Protected)
|
||||
d.Set("visibility", image.Visibility)
|
||||
d.Set("checksum", image.Checksum)
|
||||
d.Set("size_bytes", image.SizeBytes)
|
||||
d.Set("metadata", image.Metadata)
|
||||
d.Set("created_at", image.CreatedAt)
|
||||
d.Set("updated_at", image.UpdatedAt)
|
||||
d.Set("file", image.File)
|
||||
d.Set("schema", image.Schema)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type imageSort []images.Image
|
||||
|
||||
func (a imageSort) Len() int { return len(a) }
|
||||
func (a imageSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a imageSort) Less(i, j int) bool {
|
||||
itime := a[i].UpdatedAt
|
||||
jtime := a[j].UpdatedAt
|
||||
return itime.Unix() < jtime.Unix()
|
||||
}
|
||||
|
||||
// Returns the most recent Image out of a slice of images.
|
||||
func mostRecentImage(images []images.Image) images.Image {
|
||||
sortedImages := images
|
||||
sort.Sort(imageSort(sortedImages))
|
||||
return sortedImages[len(sortedImages)-1]
|
||||
}
|
||||
|
||||
func dataSourceImagesImageV2SortDirection(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(string)
|
||||
if value != "asc" && value != "desc" {
|
||||
err := fmt.Errorf("%s must be either asc or desc", k)
|
||||
errors = append(errors, err)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccOpenStackImagesV2ImageDataSource_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccOpenStackImagesV2ImageDataSource_cirros,
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccOpenStackImagesV2ImageDataSource_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckImagesV2DataSourceID("data.openstack_images_image_v2.image_1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.openstack_images_image_v2.image_1", "name", "CirrOS-tf"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.openstack_images_image_v2.image_1", "container_format", "bare"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.openstack_images_image_v2.image_1", "disk_format", "qcow2"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.openstack_images_image_v2.image_1", "min_disk_gb", "0"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.openstack_images_image_v2.image_1", "min_ram_mb", "0"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.openstack_images_image_v2.image_1", "protected", "false"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.openstack_images_image_v2.image_1", "visibility", "private"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccOpenStackImagesV2ImageDataSource_testQueries(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccOpenStackImagesV2ImageDataSource_cirros,
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccOpenStackImagesV2ImageDataSource_queryTag,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckImagesV2DataSourceID("data.openstack_images_image_v2.image_1"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccOpenStackImagesV2ImageDataSource_querySizeMin,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckImagesV2DataSourceID("data.openstack_images_image_v2.image_1"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccOpenStackImagesV2ImageDataSource_querySizeMax,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckImagesV2DataSourceID("data.openstack_images_image_v2.image_1"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckImagesV2DataSourceID(n string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Can't find image data source: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("Image data source ID not set")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Standard CirrOS image
|
||||
const testAccOpenStackImagesV2ImageDataSource_cirros = `
|
||||
resource "openstack_images_image_v2" "image_1" {
|
||||
name = "CirrOS-tf"
|
||||
container_format = "bare"
|
||||
disk_format = "qcow2"
|
||||
image_source_url = "http://download.cirros-cloud.net/0.3.5/cirros-0.3.5-x86_64-disk.img"
|
||||
tags = ["cirros-tf"]
|
||||
}
|
||||
`
|
||||
|
||||
var testAccOpenStackImagesV2ImageDataSource_basic = fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
data "openstack_images_image_v2" "image_1" {
|
||||
most_recent = true
|
||||
name = "${openstack_images_image_v2.image_1.name}"
|
||||
}
|
||||
`, testAccOpenStackImagesV2ImageDataSource_cirros)
|
||||
|
||||
var testAccOpenStackImagesV2ImageDataSource_queryTag = fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
data "openstack_images_image_v2" "image_1" {
|
||||
most_recent = true
|
||||
visibility = "private"
|
||||
tag = "cirros-tf"
|
||||
}
|
||||
`, testAccOpenStackImagesV2ImageDataSource_cirros)
|
||||
|
||||
var testAccOpenStackImagesV2ImageDataSource_querySizeMin = fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
data "openstack_images_image_v2" "image_1" {
|
||||
most_recent = true
|
||||
visibility = "private"
|
||||
size_min = "13000000"
|
||||
}
|
||||
`, testAccOpenStackImagesV2ImageDataSource_cirros)
|
||||
|
||||
var testAccOpenStackImagesV2ImageDataSource_querySizeMax = fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
data "openstack_images_image_v2" "image_1" {
|
||||
most_recent = true
|
||||
visibility = "private"
|
||||
size_max = "23000000"
|
||||
}
|
||||
`, testAccOpenStackImagesV2ImageDataSource_cirros)
|
|
@ -134,6 +134,10 @@ func Provider() terraform.ResourceProvider {
|
|||
},
|
||||
},
|
||||
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"openstack_images_image_v2": dataSourceImagesImageV2(),
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"openstack_blockstorage_volume_v1": resourceBlockStorageVolumeV1(),
|
||||
"openstack_blockstorage_volume_v2": resourceBlockStorageVolumeV2(),
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// Upload uploads image file
|
||||
func Upload(client *gophercloud.ServiceClient, id string, data io.ReadSeeker) (r UploadResult) {
|
||||
func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) {
|
||||
_, r.Err = client.Put(uploadURL(client, id), data, nil, &gophercloud.RequestOpts{
|
||||
MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"},
|
||||
OkCodes: []int{204},
|
||||
|
|
|
@ -156,6 +156,11 @@ func (r ImagePage) NextPageURL() (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if s.Next == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return nextPageURL(r.URL.String(), s.Next), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1434,16 +1434,16 @@
|
|||
"revisionTime": "2017-02-14T04:36:15Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "ApyhoqnMmvOyj4SVmCAMEPRt0CY=",
|
||||
"checksumSHA1": "5+wNKnxGvSGV8lHS+7km0ZiNEts=",
|
||||
"path": "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata",
|
||||
"revision": "b06120d13e262ceaf890ef38ee30898813696af0",
|
||||
"revisionTime": "2017-02-14T04:36:15Z"
|
||||
"revision": "f47ca3a2d457dd4601b823eb17ecc3094baf5fab",
|
||||
"revisionTime": "2017-02-17T17:23:12Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "suBNiSHEsfnRAY+kGSGg0P2TaUQ=",
|
||||
"checksumSHA1": "TG1z1hNllqjUgBpNnqZTxHqXBTs=",
|
||||
"path": "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images",
|
||||
"revision": "b06120d13e262ceaf890ef38ee30898813696af0",
|
||||
"revisionTime": "2017-02-14T04:36:15Z"
|
||||
"revision": "f47ca3a2d457dd4601b823eb17ecc3094baf5fab",
|
||||
"revisionTime": "2017-02-17T17:23:12Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "aTHxjMlfNXFJ3l2TZyvIwqt/3kM=",
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
layout: "openstack"
|
||||
page_title: "OpenStack: openstack_images_image_v2"
|
||||
sidebar_current: "docs-openstack-datasource-images-image-v2"
|
||||
description: |-
|
||||
Get information on an OpenStack Image.
|
||||
---
|
||||
|
||||
# openstack\_images\_image\_v2
|
||||
|
||||
Use this data source to get the ID of an available OpenStack image.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
data "openstack_images_image_v2" "ubuntu" {
|
||||
name = "Ubuntu 16.04"
|
||||
most_recent = true
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
* `region` - (Required) The region in which to obtain the V2 Glance client.
|
||||
A Glance client is needed to create an Image that can be used with
|
||||
a compute instance. If omitted, the `OS_REGION_NAME` environment variable
|
||||
is used.
|
||||
|
||||
* `most_recent` - (Optional) If more than one result is returned, use the most
|
||||
recent image.
|
||||
|
||||
* `name` - (Optional) The name of the image.
|
||||
|
||||
* `owner` - (Optional) The owner (UUID) of the image.
|
||||
|
||||
* `size_min` - (Optional) The minimum size (in bytes) of the image to return.
|
||||
|
||||
* `size_max` - (Optional) The maximum size (in bytes) of the image to return.
|
||||
|
||||
* `sort_direction` - (Optional) Order the results in either `asc` or `desc`.
|
||||
|
||||
* `sort_key` - (Optional) Sort images based on a certain key. Defaults to `name`.
|
||||
|
||||
* `tag` - (Optional) Search for images with a specific tag.
|
||||
|
||||
* `visibility` - (Optional) The visibility of the image. Must be one of
|
||||
"public", "private", "community", or "shared". Defaults to "private".
|
||||
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
`id` is set to the ID of the found image. In addition, the following attributes
|
||||
are exported:
|
||||
|
||||
* `checksum` - The checksum of the data associated with the image.
|
||||
* `created_at` - The date the image was created.
|
||||
* `container_format`: The format of the image's container.
|
||||
* `disk_format`: The format of the image's disk.
|
||||
* `file` - the trailing path after the glance endpoint that represent the
|
||||
location of the image or the path to retrieve it.
|
||||
* `metadata` - The metadata associated with the image.
|
||||
Image metadata allow for meaningfully define the image properties
|
||||
and tags. See http://docs.openstack.org/developer/glance/metadefs-concepts.html.
|
||||
* `min_disk_gb`: The minimum amount of disk space required to use the image.
|
||||
* `min_ram_mb`: The minimum amount of ram required to use the image.
|
||||
* `protected` - Whether or not the image is protected.
|
||||
* `schema` - The path to the JSON-schema that represent
|
||||
the image or image
|
||||
* `size_bytes` - The size of the image (in bytes).
|
||||
* `tags` - See Argument Reference above.
|
||||
* `update_at` - The date the image was last updated.
|
|
@ -10,6 +10,15 @@
|
|||
<a href="/docs/providers/openstack/index.html">OpenStack Provider</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current(/^docs-openstack-datasource/) %>>
|
||||
<a href="#">Data Sources</a>
|
||||
<ul class="nav nav-visible">
|
||||
<li<%= sidebar_current("docs-openstack-datasource-images-image-v2") %>>
|
||||
<a href="/docs/providers/openstack/d/images_image_v2.html">openstack_images_image_v2</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current(/^docs-openstack-resource-blockstorage/) %>>
|
||||
<a href="#">Block Storage Resources</a>
|
||||
<ul class="nav nav-visible">
|
||||
|
|
Loading…
Reference in New Issue