diff --git a/internal/service/ecr/image_data_source.go b/internal/service/ecr/image_data_source.go index 38f0abd6397..f514e94548d 100644 --- a/internal/service/ecr/image_data_source.go +++ b/internal/service/ecr/image_data_source.go @@ -3,6 +3,7 @@ package ecr import ( "fmt" "log" + "sort" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ecr" @@ -47,6 +48,10 @@ func DataSourceImage() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "most_recent": { + Type: schema.TypeBool, + Optional: true, + }, }, } } @@ -63,28 +68,33 @@ func dataSourceImageRead(d *schema.ResourceData, meta interface{}) error { params.RegistryId = aws.String(regId.(string)) } - imgId := ecr.ImageIdentifier{} - digest, ok := d.GetOk("image_digest") - if ok { - imgId.ImageDigest = aws.String(digest.(string)) + findMostRecent := false + + if _, ok := d.GetOk("most_recent"); ok { + findMostRecent = true } - tag, ok := d.GetOk("image_tag") - if ok { - imgId.ImageTag = aws.String(tag.(string)) + + imgId := ecr.ImageIdentifier{} + if v, ok := d.GetOk("image_digest"); ok { + if findMostRecent { + return fmt.Errorf("cannot set image_digest when most_recent is set to true") + } + imgId.ImageDigest = aws.String(v.(string)) } - if imgId.ImageDigest == nil && imgId.ImageTag == nil { - return fmt.Errorf("At least one of either image_digest or image_tag must be defined") + if v, ok := d.GetOk("image_tag"); ok { + if findMostRecent { + return fmt.Errorf("cannot set image_tag when most_recent is set to true") + } + imgId.ImageTag = aws.String(v.(string)) } - params.ImageIds = []*ecr.ImageIdentifier{&imgId} + if !findMostRecent { + 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 - }) + imageDetails, err := FindImageDetails(conn, params) if err != nil { return fmt.Errorf("Error describing ECR images: %w", err) } @@ -92,10 +102,13 @@ func dataSourceImageRead(d *schema.ResourceData, meta interface{}) error { if len(imageDetails) == 0 { return fmt.Errorf("No matching image found") } - if len(imageDetails) > 1 { + + if len(imageDetails) > 1 && !findMostRecent { return fmt.Errorf("More than one image found for tag/digest combination") } + sort.Sort(sort.Reverse(byImagePushedAt(imageDetails))) + image := imageDetails[0] d.SetId(aws.StringValue(image.ImageDigest)) diff --git a/internal/service/ecr/image_data_source_test.go b/internal/service/ecr/image_data_source_test.go index e9e83edc9c5..e4cc99dff0e 100644 --- a/internal/service/ecr/image_data_source_test.go +++ b/internal/service/ecr/image_data_source_test.go @@ -15,6 +15,7 @@ func TestAccECRImageDataSource_ecrImage(t *testing.T) { registry, repo, tag := "137112412989", "amazonlinux", "latest" resourceByTag := "data.aws_ecr_image.by_tag" resourceByDigest := "data.aws_ecr_image.by_digest" + resourceByMostRecent := "data.aws_ecr_image.by_most_recent" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -31,6 +32,9 @@ func TestAccECRImageDataSource_ecrImage(t *testing.T) { resource.TestCheckResourceAttrSet(resourceByDigest, "image_pushed_at"), resource.TestCheckResourceAttrSet(resourceByDigest, "image_size_in_bytes"), testCheckTagInImageTags(resourceByDigest, tag), + resource.TestCheckResourceAttrSet(resourceByMostRecent, "image_pushed_at"), + resource.TestCheckResourceAttrSet(resourceByMostRecent, "image_size_in_bytes"), + resource.TestCheckResourceAttrSet(resourceByMostRecent, "image_tags.#"), ), }, }, @@ -50,6 +54,12 @@ data "aws_ecr_image" "by_digest" { repository_name = data.aws_ecr_image.by_tag.repository_name image_digest = data.aws_ecr_image.by_tag.image_digest } + +data "aws_ecr_image" "by_most_recent" { + registry_id = data.aws_ecr_image.by_tag.registry_id + repository_name = data.aws_ecr_image.by_tag.repository_name + most_recent = true +} `, reg, repo, tag) } diff --git a/internal/service/ecr/repository_data_source_test.go b/internal/service/ecr/repository_data_source_test.go index 213507a4a09..a92c3708d1e 100644 --- a/internal/service/ecr/repository_data_source_test.go +++ b/internal/service/ecr/repository_data_source_test.go @@ -31,6 +31,7 @@ func TestAccECRRepositoryDataSource_basic(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "image_scanning_configuration.#", dataSourceName, "image_scanning_configuration.#"), resource.TestCheckResourceAttrPair(resourceName, "image_tag_mutability", dataSourceName, "image_tag_mutability"), resource.TestCheckResourceAttrPair(resourceName, "encryption_configuration.#", dataSourceName, "encryption_configuration.#"), + resource.TestCheckResourceAttrSet(dataSourceName, "most_recent_image_tags.#"), ), }, }, diff --git a/website/docs/d/ecr_image.html.markdown b/website/docs/d/ecr_image.html.markdown index d5f2d5605c8..3ecbbcdc2bf 100644 --- a/website/docs/d/ecr_image.html.markdown +++ b/website/docs/d/ecr_image.html.markdown @@ -25,8 +25,9 @@ The following arguments are supported: * `registry_id` - (Optional) ID of the Registry where the repository resides. * `repository_name` - (Required) Name of the ECR Repository. -* `image_digest` - (Optional) Sha256 digest of the image manifest. At least one of `image_digest` or `image_tag` must be specified. -* `image_tag` - (Optional) Tag associated with this image. At least one of `image_digest` or `image_tag` must be specified. +* `image_digest` - (Optional) Sha256 digest of the image manifest. At least one of `image_digest`, `image_tag`, or `most_recent` must be specified. +* `image_tag` - (Optional) Tag associated with this image. At least one of `image_digest`, `image_tag`, or `most_recent` must be specified. +* `most_recent` - (Optional) Return the most recently pushed image. At least one of `image_digest`, `image_tag`, or `most_recent` must be specified. ## Attributes Reference