alicloud/data_source_alicloud_images.go (450 lines of code) (raw):
package alicloud
import (
"log"
"regexp"
"sort"
"strconv"
"time"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
"github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)
func dataSourceAlicloudImages() *schema.Resource {
return &schema.Resource{
Read: dataSourceAlicloudImagesRead,
Schema: map[string]*schema.Schema{
"name_regex": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.ValidateRegexp,
},
"most_recent": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ForceNew: true,
},
"owners": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
// must contain a valid Image owner, expected ImageOwnerSystem, ImageOwnerSelf, ImageOwnerOthers, ImageOwnerMarketplace, ImageOwnerDefault
ValidateFunc: validation.StringInSlice([]string{"system", "self", "others", "marketplace", ""}, false),
},
"image_owner_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"output_file": {
Type: schema.TypeString,
Optional: true,
},
"ids": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"status": {
Type: schema.TypeString,
Optional: true,
Default: "Available",
ValidateFunc: validation.StringInSlice([]string{"Available", "Creating", "Waiting", "UnAvailable", "CreateFailed", "Deprecated"}, false),
},
"image_id": {
Type: schema.TypeString,
Optional: true,
},
"image_name": {
Type: schema.TypeString,
Optional: true,
},
"is_support_io_optimized": {
Type: schema.TypeBool,
Optional: true,
},
"is_support_cloud_init": {
Type: schema.TypeBool,
Optional: true,
},
"dry_run": {
Type: schema.TypeBool,
Optional: true,
},
"snapshot_id": {
Type: schema.TypeString,
Optional: true,
},
"image_family": {
Type: schema.TypeString,
Optional: true,
},
"instance_type": {
Type: schema.TypeString,
Optional: true,
},
"resource_group_id": {
Type: schema.TypeString,
Optional: true,
},
"usage": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"instance", "none"}, false),
},
"os_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"windows", "linux"}, false),
},
"architecture": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"i386", "x86_64"}, false),
},
"action_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"CreateEcs", "CreateOS"}, false),
},
"tags": tagsSchema(),
// Computed values.
"images": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Computed: true,
},
"image_id": {
Type: schema.TypeString,
Computed: true,
},
"architecture": {
Type: schema.TypeString,
Computed: true,
},
"creation_time": {
Type: schema.TypeString,
Computed: true,
},
"description": {
Type: schema.TypeString,
Computed: true,
},
"image_owner_alias": {
Type: schema.TypeString,
Computed: true,
},
"os_type": {
Type: schema.TypeString,
Computed: true,
},
"os_name": {
Type: schema.TypeString,
Computed: true,
},
"os_name_en": {
Type: schema.TypeString,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"platform": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"state": {
Type: schema.TypeString,
Computed: true,
},
"size": {
Type: schema.TypeInt,
Computed: true,
},
// Complex computed values
"disk_device_mappings": {
Type: schema.TypeList,
Computed: true,
//Set: imageDiskDeviceMappingHash,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"device": {
Type: schema.TypeString,
Computed: true,
},
"size": {
Type: schema.TypeString,
Computed: true,
},
"snapshot_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"product_code": {
Type: schema.TypeString,
Computed: true,
},
"is_self_shared": {
Type: schema.TypeString,
Computed: true,
},
"is_subscribed": {
Type: schema.TypeBool,
Computed: true,
},
"is_copied": {
Type: schema.TypeBool,
Computed: true,
},
"is_support_io_optimized": {
Type: schema.TypeBool,
Computed: true,
},
"image_version": {
Type: schema.TypeString,
Computed: true,
},
"progress": {
Type: schema.TypeString,
Computed: true,
},
"usage": {
Type: schema.TypeString,
Computed: true,
},
"tags": tagsSchema(),
},
},
},
},
}
}
// dataSourceAlicloudImagesDescriptionRead performs the Alicloud Image lookup.
func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
nameRegex, nameRegexOk := d.GetOk("name_regex")
owners, ownersOk := d.GetOk("owners")
mostRecent, mostRecentOk := d.GetOk("most_recent")
_, imageOwnerIdOk := d.GetOk("image_owner_id")
if !nameRegexOk && !ownersOk && !mostRecentOk && !imageOwnerIdOk {
return WrapError(Error("One of name_regex, owners, most_recent or image_owner_id must be assigned"))
}
request := ecs.CreateDescribeImagesRequest()
request.PageNumber = requests.NewInteger(1)
request.PageSize = requests.NewInteger(PageSizeXLarge)
if ownersOk {
request.ImageOwnerAlias = owners.(string)
}
if status, ok := d.GetOk("status"); ok && status.(string) != "" {
request.Status = status.(string)
}
if v, ok := d.GetOk("image_id"); ok && v.(string) != "" {
request.ImageId = v.(string)
}
if v, ok := d.GetOk("image_name"); ok && v.(string) != "" {
request.ImageName = v.(string)
}
if v, ok := d.GetOk("snapshot_id"); ok && v.(string) != "" {
request.SnapshotId = v.(string)
}
if v, ok := d.GetOk("image_family"); ok && v.(string) != "" {
request.ImageFamily = v.(string)
}
if v, ok := d.GetOk("instance_type"); ok && v.(string) != "" {
request.InstanceType = v.(string)
}
if v, ok := d.GetOk("resource_group_id"); ok && v.(string) != "" {
request.ResourceGroupId = v.(string)
}
if v, ok := d.GetOk("usage"); ok && v.(string) != "" {
request.Usage = v.(string)
}
if v, ok := d.GetOk("architecture"); ok && v.(string) != "" {
request.Architecture = v.(string)
}
if v, ok := d.GetOk("os_type"); ok && v.(string) != "" {
request.OSType = v.(string)
}
if v, ok := d.GetOk("action_type"); ok && v.(string) != "" {
request.ActionType = v.(string)
}
if v, ok := d.GetOk("is_support_io_optimized"); ok {
request.IsSupportIoOptimized = requests.NewBoolean(v.(bool))
}
if v, ok := d.GetOk("is_support_cloud_init"); ok {
request.IsSupportCloudinit = requests.NewBoolean(v.(bool))
}
if v, ok := d.GetOk("dry_run"); ok {
request.DryRun = requests.NewBoolean(v.(bool))
}
if v, ok := d.GetOk("image_owner_id"); ok {
imageId, _ := strconv.Atoi(v.(string))
request.ImageOwnerId = requests.NewInteger(imageId)
}
if v, ok := d.GetOk("tags"); ok {
var reqTags []ecs.DescribeImagesTag
for k, v := range v.(map[string]interface{}) {
reqTags = append(reqTags, ecs.DescribeImagesTag{
Key: k,
Value: v.(string),
})
}
request.Tag = &reqTags
}
var allImages []ecs.Image
var response *ecs.DescribeImagesResponse
for {
wait := incrementalWait(3*time.Second, 5*time.Second)
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.DescribeImages(request)
})
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
response, _ = raw.(*ecs.DescribeImagesResponse)
return nil
})
if err != nil {
return WrapErrorf(err, DataDefaultErrorMsg, "alicloud_images", request.GetActionName(), AlibabaCloudSdkGoERROR)
}
if response == nil || len(response.Images.Image) < 1 {
break
}
allImages = append(allImages, response.Images.Image...)
if len(response.Images.Image) < PageSizeLarge {
break
}
page, err := getNextpageNumber(request.PageNumber)
if err != nil {
return WrapError(err)
}
request.PageNumber = page
}
var filteredImages []ecs.Image
if nameRegexOk {
r, err := regexp.Compile(nameRegex.(string))
if err != nil {
return WrapError(err)
}
for _, image := range allImages {
// Check for a very rare case where the response would include no
// image name. No name means nothing to attempt a match against,
// therefore we are skipping such image.
if image.ImageName == "" {
log.Printf("[WARN] Unable to find Image name to match against "+
"for image ID %q, nothing to do.",
image.ImageId)
continue
}
if r.MatchString(image.ImageName) {
filteredImages = append(filteredImages, image)
}
}
} else {
filteredImages = allImages[:]
}
var images []ecs.Image
if len(filteredImages) > 1 && mostRecent.(bool) {
// Query returned single result.
images = append(images, mostRecentImage(filteredImages))
} else {
images = filteredImages
}
return imagesDescriptionAttributes(d, images, meta)
}
// populate the numerous fields that the image description returns.
func imagesDescriptionAttributes(d *schema.ResourceData, images []ecs.Image, meta interface{}) error {
var ids []string
var s []map[string]interface{}
for _, image := range images {
mapping := map[string]interface{}{
"id": image.ImageId,
"architecture": image.Architecture,
"creation_time": image.CreationTime,
"description": image.Description,
"image_id": image.ImageId,
"image_owner_alias": image.ImageOwnerAlias,
"os_name": image.OSName,
"os_name_en": image.OSNameEn,
"os_type": image.OSType,
"name": image.ImageName,
"platform": image.Platform,
"status": image.Status,
"state": image.Status,
"size": image.Size,
"is_self_shared": image.IsSelfShared,
"is_subscribed": image.IsSubscribed,
"is_copied": image.IsCopied,
"is_support_io_optimized": image.IsSupportIoOptimized,
"image_version": image.ImageVersion,
"progress": image.Progress,
"usage": image.Usage,
"product_code": image.ProductCode,
"disk_device_mappings": imageDiskDeviceMappings(image.DiskDeviceMappings.DiskDeviceMapping),
"tags": imageTagsMappings(d, image.ImageId, meta),
}
ids = append(ids, image.ImageId)
s = append(s, mapping)
}
d.SetId(dataResourceIdHash(ids))
if err := d.Set("images", s); err != nil {
return WrapError(err)
}
if err := d.Set("ids", ids); err != nil {
return WrapError(err)
}
// create a json file in current directory and write data source to it.
if output, ok := d.GetOk("output_file"); ok && output.(string) != "" {
writeToFile(output.(string), s)
}
return nil
}
// Find most recent image
type imageSort []ecs.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, _ := time.Parse(time.RFC3339, a[i].CreationTime)
jtime, _ := time.Parse(time.RFC3339, a[j].CreationTime)
return itime.Unix() < jtime.Unix()
}
// Returns the most recent Image out of a slice of images.
func mostRecentImage(images []ecs.Image) ecs.Image {
sortedImages := images
sort.Sort(imageSort(sortedImages))
return sortedImages[len(sortedImages)-1]
}
// Returns a set of disk device mappings.
func imageDiskDeviceMappings(m []ecs.DiskDeviceMapping) []map[string]interface{} {
var s []map[string]interface{}
for _, v := range m {
mapping := map[string]interface{}{
"device": v.Device,
"size": v.Size,
"snapshot_id": v.SnapshotId,
}
s = append(s, mapping)
}
return s
}
// Returns a mapping of image tags
func imageTagsMappings(d *schema.ResourceData, imageId string, meta interface{}) map[string]string {
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
tags, err := ecsService.DescribeTags(imageId, TagResourceImage)
if err != nil {
return nil
}
return ecsTagsToMap(tags)
}