Skip to content

feat: Add use_cache_from_previous_image variable #675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/container-image/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ module "docker_build_from_ecr" {
dir_sha = local.dir_sha
}

cache_from = ["${module.ecr.repository_url}:latest"]
cache_from = ["${module.ecr.repository_url}:latest"]
use_cache_from_previous_image = true
}

module "ecr" {
Expand Down
4 changes: 4 additions & 0 deletions modules/docker-build/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ module "docker_image" {
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.22 |
| <a name="requirement_docker"></a> [docker](#requirement\_docker) | >= 3.0 |
| <a name="requirement_external"></a> [external](#requirement\_external) | >= 2.3 |
| <a name="requirement_null"></a> [null](#requirement\_null) | >= 2.0 |

## Providers
Expand All @@ -68,6 +69,7 @@ module "docker_image" {
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.22 |
| <a name="provider_docker"></a> [docker](#provider\_docker) | >= 3.0 |
| <a name="provider_external"></a> [external](#provider\_external) | >= 2.3 |
| <a name="provider_null"></a> [null](#provider\_null) | >= 2.0 |

## Modules
Expand All @@ -85,6 +87,7 @@ No modules.
| [null_resource.sam_metadata_docker_registry_image](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
| [external_external.latest_ecr_image](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source |

## Inputs

Expand All @@ -109,6 +112,7 @@ No modules.
| <a name="input_scan_on_push"></a> [scan\_on\_push](#input\_scan\_on\_push) | Indicates whether images are scanned after being pushed to the repository | `bool` | `false` | no |
| <a name="input_source_path"></a> [source\_path](#input\_source\_path) | Path to folder containing application code | `string` | `null` | no |
| <a name="input_triggers"></a> [triggers](#input\_triggers) | A map of arbitrary strings that, when changed, will force the docker\_image resource to be replaced. This can be used to rebuild an image when contents of source code folders change | `map(string)` | `{}` | no |
| <a name="input_use_cache_from_previous_image"></a> [use\_cache\_from\_previous\_image](#input\_use\_cache\_from\_previous\_image) | If true, use the most recently pushed image in ECR as Docker cache source (cache\_from). Requires an existing ECR repo. | `bool` | `false` | no |
| <a name="input_use_image_tag"></a> [use\_image\_tag](#input\_use\_image\_tag) | Controls whether to use image tag in ECR repository URI or not. Disable this to deploy latest image using ID (sha256:...) | `bool` | `true` | no |

## Outputs
Expand Down
22 changes: 21 additions & 1 deletion modules/docker-build/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ locals {
ecr_repo = var.create_ecr_repo ? aws_ecr_repository.this[0].id : var.ecr_repo
image_tag = var.use_image_tag ? coalesce(var.image_tag, formatdate("YYYYMMDDhhmmss", timestamp())) : null
ecr_image_name = var.use_image_tag ? format("%v/%v:%v", local.ecr_address, local.ecr_repo, local.image_tag) : format("%v/%v", local.ecr_address, local.ecr_repo)

previous_image_from_ecr = try(data.external.latest_ecr_image[0].result.image_uri, "")

previous_image_list = (
var.use_cache_from_previous_image && local.previous_image_from_ecr != ""
) ? [local.previous_image_from_ecr] : []

cache_from_effective = concat(var.cache_from, local.previous_image_list)

}

resource "docker_image" "this" {
Expand All @@ -17,7 +26,7 @@ resource "docker_image" "this" {
dockerfile = var.docker_file_path
build_args = var.build_args
platform = var.platform
cache_from = var.cache_from
cache_from = local.cache_from_effective
}

force_remove = var.force_remove
Expand All @@ -33,6 +42,17 @@ resource "docker_registry_image" "this" {
triggers = length(var.triggers) == 0 ? { image_id = docker_image.this.image_id } : var.triggers
}

data "external" "latest_ecr_image" {
count = var.use_cache_from_previous_image ? 1 : 0

program = ["bash", "${path.module}/scripts/get-latest-ecr-image.sh"]

query = {
repository = var.ecr_repo
region = data.aws_region.current.name
}
}

resource "aws_ecr_repository" "this" {
count = var.create_ecr_repo ? 1 : 0

Expand Down
37 changes: 37 additions & 0 deletions modules/docker-build/scripts/get-latest-ecr-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash

read -r INPUT
REPO=$(echo "$INPUT" | jq -r '.repository // empty')
REGION=$(echo "$INPUT" | jq -r '.region // empty')

if [[ -z "$REPO" || -z "$REGION" ]]; then
echo '{"image_uri": ""}'
exit 0
fi

# Check if repo exists
if ! aws ecr describe-repositories --repository-names "$REPO" --region "$REGION" >/dev/null 2>&1; then
echo 'no{"image_uri": ""}'
exit 0
fi

# Get latest image tag
IMAGE=$(aws ecr describe-images \
--repository-name "$REPO" \
--region "$REGION" \
--query 'reverse(sort_by(imageDetails, &imagePushedAt))[?imageTags]|[0].imageTags[0]' \
--output text 2>/dev/null || echo "")

if [ -z "$IMAGE" ] || [ "$IMAGE" == "None" ]; then
echo '{"image_uri": ""}'
exit 0
fi

# Get full image URI
URI=$(aws ecr describe-repositories \
--repository-names "$REPO" \
--region "$REGION" \
--query 'repositories[0].repositoryUri' \
--output text)

echo "{\"image_uri\": \"${URI}:${IMAGE}\"}"
6 changes: 6 additions & 0 deletions modules/docker-build/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,9 @@ variable "cache_from" {
type = list(string)
default = []
}

variable "use_cache_from_previous_image" {
description = "If true, use the most recently pushed image in ECR as Docker cache source (cache_from). Requires an existing ECR repo."
type = bool
default = false
}
4 changes: 4 additions & 0 deletions modules/docker-build/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ terraform {
source = "hashicorp/null"
version = ">= 2.0"
}
external = {
source = "hashicorp/external"
version = ">= 2.3"
}
}
}
41 changes: 21 additions & 20 deletions wrappers/docker-build/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@ module "wrapper" {

for_each = var.items

build_args = try(each.value.build_args, var.defaults.build_args, {})
cache_from = try(each.value.cache_from, var.defaults.cache_from, [])
create_ecr_repo = try(each.value.create_ecr_repo, var.defaults.create_ecr_repo, false)
create_sam_metadata = try(each.value.create_sam_metadata, var.defaults.create_sam_metadata, false)
docker_file_path = try(each.value.docker_file_path, var.defaults.docker_file_path, "Dockerfile")
ecr_address = try(each.value.ecr_address, var.defaults.ecr_address, null)
ecr_force_delete = try(each.value.ecr_force_delete, var.defaults.ecr_force_delete, true)
ecr_repo = try(each.value.ecr_repo, var.defaults.ecr_repo, null)
ecr_repo_lifecycle_policy = try(each.value.ecr_repo_lifecycle_policy, var.defaults.ecr_repo_lifecycle_policy, null)
ecr_repo_tags = try(each.value.ecr_repo_tags, var.defaults.ecr_repo_tags, {})
force_remove = try(each.value.force_remove, var.defaults.force_remove, false)
image_tag = try(each.value.image_tag, var.defaults.image_tag, null)
image_tag_mutability = try(each.value.image_tag_mutability, var.defaults.image_tag_mutability, "MUTABLE")
keep_locally = try(each.value.keep_locally, var.defaults.keep_locally, false)
keep_remotely = try(each.value.keep_remotely, var.defaults.keep_remotely, false)
platform = try(each.value.platform, var.defaults.platform, null)
scan_on_push = try(each.value.scan_on_push, var.defaults.scan_on_push, false)
source_path = try(each.value.source_path, var.defaults.source_path, null)
triggers = try(each.value.triggers, var.defaults.triggers, {})
use_image_tag = try(each.value.use_image_tag, var.defaults.use_image_tag, true)
build_args = try(each.value.build_args, var.defaults.build_args, {})
cache_from = try(each.value.cache_from, var.defaults.cache_from, [])
create_ecr_repo = try(each.value.create_ecr_repo, var.defaults.create_ecr_repo, false)
create_sam_metadata = try(each.value.create_sam_metadata, var.defaults.create_sam_metadata, false)
docker_file_path = try(each.value.docker_file_path, var.defaults.docker_file_path, "Dockerfile")
ecr_address = try(each.value.ecr_address, var.defaults.ecr_address, null)
ecr_force_delete = try(each.value.ecr_force_delete, var.defaults.ecr_force_delete, true)
ecr_repo = try(each.value.ecr_repo, var.defaults.ecr_repo, null)
ecr_repo_lifecycle_policy = try(each.value.ecr_repo_lifecycle_policy, var.defaults.ecr_repo_lifecycle_policy, null)
ecr_repo_tags = try(each.value.ecr_repo_tags, var.defaults.ecr_repo_tags, {})
force_remove = try(each.value.force_remove, var.defaults.force_remove, false)
image_tag = try(each.value.image_tag, var.defaults.image_tag, null)
image_tag_mutability = try(each.value.image_tag_mutability, var.defaults.image_tag_mutability, "MUTABLE")
keep_locally = try(each.value.keep_locally, var.defaults.keep_locally, false)
keep_remotely = try(each.value.keep_remotely, var.defaults.keep_remotely, false)
platform = try(each.value.platform, var.defaults.platform, null)
scan_on_push = try(each.value.scan_on_push, var.defaults.scan_on_push, false)
source_path = try(each.value.source_path, var.defaults.source_path, null)
triggers = try(each.value.triggers, var.defaults.triggers, {})
use_cache_from_previous_image = try(each.value.use_cache_from_previous_image, var.defaults.use_cache_from_previous_image, false)
use_image_tag = try(each.value.use_image_tag, var.defaults.use_image_tag, true)
}
4 changes: 4 additions & 0 deletions wrappers/docker-build/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ terraform {
source = "hashicorp/null"
version = ">= 2.0"
}
external = {
source = "hashicorp/external"
version = ">= 2.3"
}
}
}