From 83d482992b6c42bea36d94f14b1da6616dc81ad1 Mon Sep 17 00:00:00 2001 From: AlexRudd Date: Thu, 11 Apr 2019 20:04:47 +0100 Subject: [PATCH] Add 'aws_ecr_image' datasource --- aws/data_source_aws_ecr_image.go | 124 +++++++++++++++++++++++++ aws/data_source_aws_ecr_image_test.go | 75 +++++++++++++++ aws/provider.go | 1 + website/aws.erb | 3 + website/docs/d/ecr_image.html.markdown | 37 ++++++++ 5 files changed, 240 insertions(+) create mode 100755 aws/data_source_aws_ecr_image.go create mode 100644 aws/data_source_aws_ecr_image_test.go create mode 100644 website/docs/d/ecr_image.html.markdown diff --git a/aws/data_source_aws_ecr_image.go b/aws/data_source_aws_ecr_image.go new file mode 100755 index 00000000000..1c5b8ce259d --- /dev/null +++ b/aws/data_source_aws_ecr_image.go @@ -0,0 +1,124 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ecr" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func dataSourceAwsEcrImage() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsEcrImageRead, + Schema: map[string]*schema.Schema{ + "registry_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.NoZeroValues, + }, + "repository_name": { + Type: schema.TypeString, + Required: true, + }, + "image_digest": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + "image_tag": { + Type: schema.TypeString, + Optional: true, + }, + "image_pushed_at": { + Type: schema.TypeInt, + Computed: true, + }, + "image_size_in_bytes": { + Type: schema.TypeInt, + Computed: true, + }, + "image_tags": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func dataSourceAwsEcrImageRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ecrconn + + params := &ecr.DescribeImagesInput{ + RepositoryName: aws.String(d.Get("repository_name").(string)), + } + + regId, ok := d.GetOk("registry_id") + if ok { + params.RegistryId = aws.String(regId.(string)) + } + + imgId := ecr.ImageIdentifier{} + digest, ok := d.GetOk("image_digest") + if ok { + imgId.ImageDigest = aws.String(digest.(string)) + } + tag, ok := d.GetOk("image_tag") + if ok { + imgId.ImageTag = aws.String(tag.(string)) + } + + if imgId.ImageDigest == nil && imgId.ImageTag == nil { + return fmt.Errorf("At least one of either image_digest or image_tag must be defined") + } + + params.ImageIds = []*ecr.ImageIdentifier{&imgId} + + var imageDetails []*ecr.ImageDetail + log.Printf("[DEBUG] Reading ECR Images: %s", params) + err := conn.DescribeImagesPages(params, func(page *ecr.DescribeImagesOutput, lastPage bool) bool { + imageDetails = append(imageDetails, page.ImageDetails...) + return true + }) + if err != nil { + return fmt.Errorf("Error describing ECR images: %q", err) + } + + if len(imageDetails) == 0 { + return fmt.Errorf("No matching image found") + } + if len(imageDetails) > 1 { + return fmt.Errorf("More than one image found for tag/digest combination") + } + + image := imageDetails[0] + + d.SetId(time.Now().UTC().String()) + if err = d.Set("registry_id", *image.RegistryId); err != nil { + return fmt.Errorf("failed to set registry_id: %s", err) + } + if err = d.Set("image_digest", *image.ImageDigest); err != nil { + return fmt.Errorf("failed to set image_digest: %s", err) + } + if err = d.Set("image_pushed_at", image.ImagePushedAt.Unix()); err != nil { + return fmt.Errorf("failed to set image_pushed_at: %s", err) + } + if err = d.Set("image_size_in_bytes", *image.ImageSizeInBytes); err != nil { + return fmt.Errorf("failed to set image_size_in_bytes: %s", err) + } + + tags := []string{} + for _, t := range image.ImageTags { + tags = append(tags, *t) + } + if err := d.Set("image_tags", tags); err != nil { + return fmt.Errorf("failed to set image_tags: %s", err) + } + + return nil +} diff --git a/aws/data_source_aws_ecr_image_test.go b/aws/data_source_aws_ecr_image_test.go new file mode 100644 index 00000000000..63fcc342fc9 --- /dev/null +++ b/aws/data_source_aws_ecr_image_test.go @@ -0,0 +1,75 @@ +package aws + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSEcrDataSource_ecrImage(t *testing.T) { + registry, repo, tag := "137112412989", "amazonlinux", "latest" + resourceByTag := "data.aws_ecr_image.by_tag" + resourceByDigest := "data.aws_ecr_image.by_digest" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAwsEcrImageDataSourceConfig(registry, repo, tag), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceByTag, "image_digest"), + resource.TestCheckResourceAttrSet(resourceByTag, "image_pushed_at"), + resource.TestCheckResourceAttrSet(resourceByTag, "image_size_in_bytes"), + testCheckTagInImageTags(resourceByTag, tag), + resource.TestCheckResourceAttrSet(resourceByDigest, "image_pushed_at"), + resource.TestCheckResourceAttrSet(resourceByDigest, "image_size_in_bytes"), + testCheckTagInImageTags(resourceByDigest, tag), + ), + }, + }, + }) +} + +func testAccCheckAwsEcrImageDataSourceConfig(reg, repo, tag string) string { + return fmt.Sprintf(` +data "aws_ecr_image" "by_tag" { + registry_id = "%s" + repository_name = "%s" + image_tag = "%s" +} + +data "aws_ecr_image" "by_digest" { + registry_id = "${data.aws_ecr_image.by_tag.registry_id}" + repository_name = "${data.aws_ecr_image.by_tag.repository_name}" + image_digest = "${data.aws_ecr_image.by_tag.image_digest}" +} +`, reg, repo, tag) +} + +func testCheckTagInImageTags(name, expectedTag string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Resource not found: %s", name) + } + + tagsLenStr, ok := rs.Primary.Attributes["image_tags.#"] + if !ok { + return fmt.Errorf("No attribute 'image_tags' in resource: %s", name) + } + tagsLen, _ := strconv.Atoi(tagsLenStr) + + for i := 0; i < tagsLen; i++ { + tag := rs.Primary.Attributes[fmt.Sprintf("image_tags.%d", i)] + if tag == expectedTag { + return nil + } + } + return fmt.Errorf("No tag '%s' in images_tags of resource %s", expectedTag, name) + } +} diff --git a/aws/provider.go b/aws/provider.go index 8895658307f..be8b7b4e958 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -180,6 +180,7 @@ func Provider() terraform.ResourceProvider { "aws_ec2_transit_gateway_route_table": dataSourceAwsEc2TransitGatewayRouteTable(), "aws_ec2_transit_gateway_vpc_attachment": dataSourceAwsEc2TransitGatewayVpcAttachment(), "aws_ec2_transit_gateway_vpn_attachment": dataSourceAwsEc2TransitGatewayVpnAttachment(), + "aws_ecr_image": dataSourceAwsEcrImage(), "aws_ecr_repository": dataSourceAwsEcrRepository(), "aws_ecs_cluster": dataSourceAwsEcsCluster(), "aws_ecs_container_definition": dataSourceAwsEcsContainerDefinition(), diff --git a/website/aws.erb b/website/aws.erb index 93adc9b69a3..e898fed9c15 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -161,6 +161,9 @@
  • aws_ec2_transit_gateway_vpn_attachment
  • +
  • + aws_ecr_image +
  • aws_ecr_repository
  • diff --git a/website/docs/d/ecr_image.html.markdown b/website/docs/d/ecr_image.html.markdown new file mode 100644 index 00000000000..234fa3aa99b --- /dev/null +++ b/website/docs/d/ecr_image.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "aws" +page_title: "AWS: aws_ecr_image" +sidebar_current: "docs-aws-datasource-ecr-image" +description: |- + Provides details about an ECR Image +--- + +# Data Source: aws_ecr_image + +The ECR Image data source allows the details of an image with a particular tag or digest to be retrieved. + +## Example Usage + +```hcl +data "aws_ecr_image" "service_image" { + repository_name = "my/service" + image_tag = "latest" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `registry_id` - (Optional) The ID of the Registry where the repository resides. +* `repository_name` - (Required) The name of the ECR Repository. +* `image_digest` - (Optional) The sha256 digest of the image manifest. At least one of `image_digest` or `image_tag` must be specified. +* `image_tag` - (Optional) The tag associated with this image. At least one of `image_digest` or `image_tag` must be specified. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `image_pushed_at` - The date and time, expressed as a unix timestamp, at which the current image was pushed to the repository. +* `image_size_in_bytes` - The size, in bytes, of the image in the repository. +* `image_tags` - The list of tags associated with this image.