Skip to content

Commit 724087d

Browse files
feat: add use_cache_from_previous_image variable
1 parent dedc736 commit 724087d

File tree

7 files changed

+115
-43
lines changed

7 files changed

+115
-43
lines changed

examples/container-image/main.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ module "docker_build_from_ecr" {
129129
}
130130

131131
cache_from = ["${module.ecr.repository_url}:latest"]
132+
use_cache_from_previous_image = true
132133
}
133134

134135
module "ecr" {

modules/docker-build/README.md

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -88,28 +88,29 @@ No modules.
8888

8989
## Inputs
9090

91-
| Name | Description | Type | Default | Required |
92-
|------|-------------|------|---------|:--------:|
93-
| <a name="input_build_args"></a> [build\_args](#input\_build\_args) | A map of Docker build arguments. | `map(string)` | `{}` | no |
94-
| <a name="input_cache_from"></a> [cache\_from](#input\_cache\_from) | List of images to consider as cache sources when building the image. | `list(string)` | `[]` | no |
95-
| <a name="input_create_ecr_repo"></a> [create\_ecr\_repo](#input\_create\_ecr\_repo) | Controls whether ECR repository for Lambda image should be created | `bool` | `false` | no |
96-
| <a name="input_create_sam_metadata"></a> [create\_sam\_metadata](#input\_create\_sam\_metadata) | Controls whether the SAM metadata null resource should be created | `bool` | `false` | no |
97-
| <a name="input_docker_file_path"></a> [docker\_file\_path](#input\_docker\_file\_path) | Path to Dockerfile in source package | `string` | `"Dockerfile"` | no |
98-
| <a name="input_ecr_address"></a> [ecr\_address](#input\_ecr\_address) | Address of ECR repository for cross-account container image pulling (optional). Option `create_ecr_repo` must be `false` | `string` | `null` | no |
99-
| <a name="input_ecr_force_delete"></a> [ecr\_force\_delete](#input\_ecr\_force\_delete) | If true, will delete the repository even if it contains images. | `bool` | `true` | no |
100-
| <a name="input_ecr_repo"></a> [ecr\_repo](#input\_ecr\_repo) | Name of ECR repository to use or to create | `string` | `null` | no |
101-
| <a name="input_ecr_repo_lifecycle_policy"></a> [ecr\_repo\_lifecycle\_policy](#input\_ecr\_repo\_lifecycle\_policy) | A JSON formatted ECR lifecycle policy to automate the cleaning up of unused images. | `string` | `null` | no |
102-
| <a name="input_ecr_repo_tags"></a> [ecr\_repo\_tags](#input\_ecr\_repo\_tags) | A map of tags to assign to ECR repository | `map(string)` | `{}` | no |
103-
| <a name="input_force_remove"></a> [force\_remove](#input\_force\_remove) | Whether to remove image forcibly when the resource is destroyed. | `bool` | `false` | no |
104-
| <a name="input_image_tag"></a> [image\_tag](#input\_image\_tag) | Image tag to use. If not specified current timestamp in format 'YYYYMMDDhhmmss' will be used. This can lead to unnecessary rebuilds. | `string` | `null` | no |
105-
| <a name="input_image_tag_mutability"></a> [image\_tag\_mutability](#input\_image\_tag\_mutability) | The tag mutability setting for the repository. Must be one of: `MUTABLE` or `IMMUTABLE` | `string` | `"MUTABLE"` | no |
106-
| <a name="input_keep_locally"></a> [keep\_locally](#input\_keep\_locally) | Whether to delete the Docker image locally on destroy operation. | `bool` | `false` | no |
107-
| <a name="input_keep_remotely"></a> [keep\_remotely](#input\_keep\_remotely) | Whether to keep Docker image in the remote registry on destroy operation. | `bool` | `false` | no |
108-
| <a name="input_platform"></a> [platform](#input\_platform) | The target architecture platform to build the image for. | `string` | `null` | no |
109-
| <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 |
110-
| <a name="input_source_path"></a> [source\_path](#input\_source\_path) | Path to folder containing application code | `string` | `null` | no |
111-
| <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 |
112-
| <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 |
91+
| Name | Description | Type | Default | Required |
92+
|---------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|----------------|:--------:|
93+
| <a name="input_build_args"></a> [build\_args](#input\_build\_args) | A map of Docker build arguments. | `map(string)` | `{}` | no |
94+
| <a name="input_cache_from"></a> [cache\_from](#input\_cache\_from) | List of images to consider as cache sources when building the image. | `list(string)` | `[]` | no |
95+
| <a name="input_cache_from"></a> [use\_cache\_from\_previous\_image](#input\_cache\_from) | Use cache from previous build. Option `create_ecr_repo` must be `false` | `bool` | `false` | no |
96+
| <a name="input_create_ecr_repo"></a> [create\_ecr\_repo](#input\_create\_ecr\_repo) | Controls whether ECR repository for Lambda image should be created | `bool` | `false` | no |
97+
| <a name="input_create_sam_metadata"></a> [create\_sam\_metadata](#input\_create\_sam\_metadata) | Controls whether the SAM metadata null resource should be created | `bool` | `false` | no |
98+
| <a name="input_docker_file_path"></a> [docker\_file\_path](#input\_docker\_file\_path) | Path to Dockerfile in source package | `string` | `"Dockerfile"` | no |
99+
| <a name="input_ecr_address"></a> [ecr\_address](#input\_ecr\_address) | Address of ECR repository for cross-account container image pulling (optional). Option `create_ecr_repo` must be `false` | `string` | `null` | no |
100+
| <a name="input_ecr_force_delete"></a> [ecr\_force\_delete](#input\_ecr\_force\_delete) | If true, will delete the repository even if it contains images. | `bool` | `true` | no |
101+
| <a name="input_ecr_repo"></a> [ecr\_repo](#input\_ecr\_repo) | Name of ECR repository to use or to create | `string` | `null` | no |
102+
| <a name="input_ecr_repo_lifecycle_policy"></a> [ecr\_repo\_lifecycle\_policy](#input\_ecr\_repo\_lifecycle\_policy) | A JSON formatted ECR lifecycle policy to automate the cleaning up of unused images. | `string` | `null` | no |
103+
| <a name="input_ecr_repo_tags"></a> [ecr\_repo\_tags](#input\_ecr\_repo\_tags) | A map of tags to assign to ECR repository | `map(string)` | `{}` | no |
104+
| <a name="input_force_remove"></a> [force\_remove](#input\_force\_remove) | Whether to remove image forcibly when the resource is destroyed. | `bool` | `false` | no |
105+
| <a name="input_image_tag"></a> [image\_tag](#input\_image\_tag) | Image tag to use. If not specified current timestamp in format 'YYYYMMDDhhmmss' will be used. This can lead to unnecessary rebuilds. | `string` | `null` | no |
106+
| <a name="input_image_tag_mutability"></a> [image\_tag\_mutability](#input\_image\_tag\_mutability) | The tag mutability setting for the repository. Must be one of: `MUTABLE` or `IMMUTABLE` | `string` | `"MUTABLE"` | no |
107+
| <a name="input_keep_locally"></a> [keep\_locally](#input\_keep\_locally) | Whether to delete the Docker image locally on destroy operation. | `bool` | `false` | no |
108+
| <a name="input_keep_remotely"></a> [keep\_remotely](#input\_keep\_remotely) | Whether to keep Docker image in the remote registry on destroy operation. | `bool` | `false` | no |
109+
| <a name="input_platform"></a> [platform](#input\_platform) | The target architecture platform to build the image for. | `string` | `null` | no |
110+
| <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 |
111+
| <a name="input_source_path"></a> [source\_path](#input\_source\_path) | Path to folder containing application code | `string` | `null` | no |
112+
| <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 |
113+
| <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 |
113114

114115
## Outputs
115116

modules/docker-build/main.tf

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ locals {
77
ecr_repo = var.create_ecr_repo ? aws_ecr_repository.this[0].id : var.ecr_repo
88
image_tag = var.use_image_tag ? coalesce(var.image_tag, formatdate("YYYYMMDDhhmmss", timestamp())) : null
99
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)
10+
11+
previous_image_from_ecr = try(data.external.latest_ecr_image[0].result.image_uri, "")
12+
13+
previous_image_list = (
14+
var.use_cache_from_previous_image && local.previous_image_from_ecr != ""
15+
) ? [local.previous_image_from_ecr] : []
16+
17+
cache_from_effective = concat(var.cache_from, local.previous_image_list)
18+
1019
}
1120

1221
resource "docker_image" "this" {
@@ -17,7 +26,7 @@ resource "docker_image" "this" {
1726
dockerfile = var.docker_file_path
1827
build_args = var.build_args
1928
platform = var.platform
20-
cache_from = var.cache_from
29+
cache_from = local.cache_from_effective
2130
}
2231

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

45+
data "external" "latest_ecr_image" {
46+
count = var.use_cache_from_previous_image ? 1 : 0
47+
48+
program = ["bash", "${path.module}/scripts/get-latest-ecr-image.sh"]
49+
50+
query = {
51+
repository = var.ecr_repo
52+
region = data.aws_region.current.name
53+
}
54+
}
55+
3656
resource "aws_ecr_repository" "this" {
3757
count = var.create_ecr_repo ? 1 : 0
3858

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
3+
# Lecture de l'entrée JSON
4+
read -r INPUT
5+
REPO=$(echo "$INPUT" | jq -r '.repository // empty')
6+
REGION=$(echo "$INPUT" | jq -r '.region // empty')
7+
8+
if [[ -z "$REPO" || -z "$REGION" ]]; then
9+
echo '{"image_uri": ""}'
10+
exit 0
11+
fi
12+
13+
# Check if repo exists
14+
if ! aws ecr describe-repositories --repository-names "$REPO" --region "$REGION" >/dev/null 2>&1; then
15+
echo 'no{"image_uri": ""}'
16+
exit 0
17+
fi
18+
19+
# Get latest image tag
20+
IMAGE=$(aws ecr describe-images \
21+
--repository-name "$REPO" \
22+
--region "$REGION" \
23+
--query 'reverse(sort_by(imageDetails, &imagePushedAt))[?imageTags]|[0].imageTags[0]' \
24+
--output text 2>/dev/null || echo "")
25+
26+
if [ -z "$IMAGE" ] || [ "$IMAGE" == "None" ]; then
27+
echo '{"image_uri": ""}'
28+
exit 0
29+
fi
30+
31+
# Get full image URI
32+
URI=$(aws ecr describe-repositories \
33+
--repository-names "$REPO" \
34+
--region "$REGION" \
35+
--query 'repositories[0].repositoryUri' \
36+
--output text)
37+
38+
echo "{\"image_uri\": \"${URI}:${IMAGE}\"}"

modules/docker-build/variables.tf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,10 @@ variable "cache_from" {
118118
type = list(string)
119119
default = []
120120
}
121+
122+
variable "use_cache_from_previous_image" {
123+
description = "If true, use the most recently pushed image in ECR as Docker cache source (cache_from). Requires an existing ECR repo."
124+
type = bool
125+
default = false
126+
}
127+

modules/docker-build/versions.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@ terraform {
1414
source = "hashicorp/null"
1515
version = ">= 2.0"
1616
}
17+
external = {
18+
source = "hashicorp/external"
19+
version = ">= 2.3"
20+
}
1721
}
1822
}

wrappers/docker-build/main.tf

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,25 @@ module "wrapper" {
33

44
for_each = var.items
55

6-
build_args = try(each.value.build_args, var.defaults.build_args, {})
7-
cache_from = try(each.value.cache_from, var.defaults.cache_from, [])
8-
create_ecr_repo = try(each.value.create_ecr_repo, var.defaults.create_ecr_repo, false)
9-
create_sam_metadata = try(each.value.create_sam_metadata, var.defaults.create_sam_metadata, false)
10-
docker_file_path = try(each.value.docker_file_path, var.defaults.docker_file_path, "Dockerfile")
11-
ecr_address = try(each.value.ecr_address, var.defaults.ecr_address, null)
12-
ecr_force_delete = try(each.value.ecr_force_delete, var.defaults.ecr_force_delete, true)
13-
ecr_repo = try(each.value.ecr_repo, var.defaults.ecr_repo, null)
14-
ecr_repo_lifecycle_policy = try(each.value.ecr_repo_lifecycle_policy, var.defaults.ecr_repo_lifecycle_policy, null)
15-
ecr_repo_tags = try(each.value.ecr_repo_tags, var.defaults.ecr_repo_tags, {})
16-
force_remove = try(each.value.force_remove, var.defaults.force_remove, false)
17-
image_tag = try(each.value.image_tag, var.defaults.image_tag, null)
18-
image_tag_mutability = try(each.value.image_tag_mutability, var.defaults.image_tag_mutability, "MUTABLE")
19-
keep_locally = try(each.value.keep_locally, var.defaults.keep_locally, false)
20-
keep_remotely = try(each.value.keep_remotely, var.defaults.keep_remotely, false)
21-
platform = try(each.value.platform, var.defaults.platform, null)
22-
scan_on_push = try(each.value.scan_on_push, var.defaults.scan_on_push, false)
23-
source_path = try(each.value.source_path, var.defaults.source_path, null)
24-
triggers = try(each.value.triggers, var.defaults.triggers, {})
25-
use_image_tag = try(each.value.use_image_tag, var.defaults.use_image_tag, true)
6+
build_args = try(each.value.build_args, var.defaults.build_args, {})
7+
use_cache_from_previous_image = try(each.value.use_cache_from_previous_image, var.defaults.use_cache_from_previous_image, false)
8+
cache_from = try(each.value.cache_from, var.defaults.cache_from, [])
9+
create_ecr_repo = try(each.value.create_ecr_repo, var.defaults.create_ecr_repo, false)
10+
create_sam_metadata = try(each.value.create_sam_metadata, var.defaults.create_sam_metadata, false)
11+
docker_file_path = try(each.value.docker_file_path, var.defaults.docker_file_path, "Dockerfile")
12+
ecr_address = try(each.value.ecr_address, var.defaults.ecr_address, null)
13+
ecr_force_delete = try(each.value.ecr_force_delete, var.defaults.ecr_force_delete, true)
14+
ecr_repo = try(each.value.ecr_repo, var.defaults.ecr_repo, null)
15+
ecr_repo_lifecycle_policy = try(each.value.ecr_repo_lifecycle_policy, var.defaults.ecr_repo_lifecycle_policy, null)
16+
ecr_repo_tags = try(each.value.ecr_repo_tags, var.defaults.ecr_repo_tags, {})
17+
force_remove = try(each.value.force_remove, var.defaults.force_remove, false)
18+
image_tag = try(each.value.image_tag, var.defaults.image_tag, null)
19+
image_tag_mutability = try(each.value.image_tag_mutability, var.defaults.image_tag_mutability, "MUTABLE")
20+
keep_locally = try(each.value.keep_locally, var.defaults.keep_locally, false)
21+
keep_remotely = try(each.value.keep_remotely, var.defaults.keep_remotely, false)
22+
platform = try(each.value.platform, var.defaults.platform, null)
23+
scan_on_push = try(each.value.scan_on_push, var.defaults.scan_on_push, false)
24+
source_path = try(each.value.source_path, var.defaults.source_path, null)
25+
triggers = try(each.value.triggers, var.defaults.triggers, {})
26+
use_image_tag = try(each.value.use_image_tag, var.defaults.use_image_tag, true)
2627
}

0 commit comments

Comments
 (0)