diff --git a/examples/container-image/README.md b/examples/container-image/README.md index 22e0d071..af9c7e86 100644 --- a/examples/container-image/README.md +++ b/examples/container-image/README.md @@ -55,6 +55,8 @@ No inputs. | Name | Description | |------|-------------| +| [docker\_image\_files\_to\_hash](#output\_docker\_image\_files\_to\_hash) | List of files used to hash the docker image tag | +| [docker\_image\_id](#output\_docker\_image\_id) | The ID of the Docker image | | [docker\_image\_uri](#output\_docker\_image\_uri) | The ECR Docker image URI used to deploy Lambda Function | | [lambda\_cloudwatch\_log\_group\_arn](#output\_lambda\_cloudwatch\_log\_group\_arn) | The ARN of the Cloudwatch Log Group | | [lambda\_function\_arn](#output\_lambda\_function\_arn) | The ARN of the Lambda Function | diff --git a/examples/container-image/main.tf b/examples/container-image/main.tf index d19be410..46c1bf77 100644 --- a/examples/container-image/main.tf +++ b/examples/container-image/main.tf @@ -4,6 +4,17 @@ data "aws_caller_identity" "this" {} data "aws_ecr_authorization_token" "token" {} +locals { + source_path = "context" + path_include = ["**"] + path_exclude = ["**/__pycache__/**"] + files_include = setunion([for f in local.path_include : fileset(local.source_path, f)]...) + files_exclude = setunion([for f in local.path_exclude : fileset(local.source_path, f)]...) + files = sort(setsubtract(local.files_include, local.files_exclude)) + + dir_sha = sha1(join("", [for f in local.files : filesha1("${local.source_path}/${f}")])) +} + provider "aws" { region = "eu-west-1" @@ -32,9 +43,10 @@ module "lambda_function_from_container_image" { ################## # Container Image ################## - image_uri = module.docker_image.image_uri package_type = "Image" - architectures = ["x86_64"] + architectures = ["arm64"] # ["x86_64"] + + image_uri = module.docker_image.image_uri } module "docker_image" { @@ -59,12 +71,20 @@ module "docker_image" { ] }) - image_tag = "2.0" - source_path = "context" + use_image_tag = false # If false, sha of the image will be used + + # use_image_tag = true + # image_tag = "2.0" + + source_path = local.source_path + platform = "linux/amd64" build_args = { FOO = "bar" } - platform = "linux/amd64" + + triggers = { + dir_sha = local.dir_sha + } } resource "random_pet" "this" { diff --git a/examples/container-image/outputs.tf b/examples/container-image/outputs.tf index a71816f8..34755681 100644 --- a/examples/container-image/outputs.tf +++ b/examples/container-image/outputs.tf @@ -97,3 +97,13 @@ output "docker_image_uri" { description = "The ECR Docker image URI used to deploy Lambda Function" value = module.docker_image.image_uri } + +output "docker_image_id" { + description = "The ID of the Docker image" + value = module.docker_image.image_id +} + +output "docker_image_files_to_hash" { + description = "List of files used to hash the docker image tag" + value = local.files +} diff --git a/modules/docker-build/README.md b/modules/docker-build/README.md index 0cf1cc81..44464404 100644 --- a/modules/docker-build/README.md +++ b/modules/docker-build/README.md @@ -34,7 +34,10 @@ module "docker_image" { create_ecr_repo = true ecr_repo = "my-cool-ecr-repo" - image_tag = "1.0" + + use_image_tag = true + image_tag = "1.0" + source_path = "context" build_args = { FOO = "bar" @@ -94,17 +97,22 @@ No modules. | [ecr\_repo](#input\_ecr\_repo) | Name of ECR repository to use or to create | `string` | `null` | no | | [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 | | [ecr\_repo\_tags](#input\_ecr\_repo\_tags) | A map of tags to assign to ECR repository | `map(string)` | `{}` | no | +| [force\_remove](#input\_force\_remove) | Whether to remove image forcibly when the resource is destroyed. | `bool` | `false` | no | | [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 | | [image\_tag\_mutability](#input\_image\_tag\_mutability) | The tag mutability setting for the repository. Must be one of: `MUTABLE` or `IMMUTABLE` | `string` | `"MUTABLE"` | no | +| [keep\_locally](#input\_keep\_locally) | Whether to delete the Docker image locally on destroy operation. | `bool` | `false` | no | | [keep\_remotely](#input\_keep\_remotely) | Whether to keep Docker image in the remote registry on destroy operation. | `bool` | `false` | no | | [platform](#input\_platform) | The target architecture platform to build the image for. | `string` | `null` | no | | [scan\_on\_push](#input\_scan\_on\_push) | Indicates whether images are scanned after being pushed to the repository | `bool` | `false` | no | | [source\_path](#input\_source\_path) | Path to folder containing application code | `string` | `null` | no | +| [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 | +| [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 | Name | Description | |------|-------------| +| [image\_id](#output\_image\_id) | The ID of the Docker image | | [image\_uri](#output\_image\_uri) | The ECR image URI for deploying lambda | diff --git a/modules/docker-build/main.tf b/modules/docker-build/main.tf index dc9ad310..b75ae05c 100644 --- a/modules/docker-build/main.tf +++ b/modules/docker-build/main.tf @@ -5,8 +5,8 @@ data "aws_caller_identity" "this" {} locals { ecr_address = coalesce(var.ecr_address, format("%v.dkr.ecr.%v.amazonaws.com", data.aws_caller_identity.this.account_id, data.aws_region.current.name)) ecr_repo = var.create_ecr_repo ? aws_ecr_repository.this[0].id : var.ecr_repo - image_tag = coalesce(var.image_tag, formatdate("YYYYMMDDhhmmss", timestamp())) - ecr_image_name = format("%v/%v:%v", local.ecr_address, local.ecr_repo, local.image_tag) + 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) } resource "docker_image" "this" { @@ -18,12 +18,20 @@ resource "docker_image" "this" { build_args = var.build_args platform = var.platform } + + force_remove = var.force_remove + keep_locally = var.keep_locally + triggers = var.triggers } resource "docker_registry_image" "this" { name = docker_image.this.name keep_remotely = var.keep_remotely + + triggers = { + image_id = docker_image.this.image_id + } } resource "aws_ecr_repository" "this" { diff --git a/modules/docker-build/outputs.tf b/modules/docker-build/outputs.tf index 05c9063a..5b268b54 100644 --- a/modules/docker-build/outputs.tf +++ b/modules/docker-build/outputs.tf @@ -1,4 +1,9 @@ output "image_uri" { description = "The ECR image URI for deploying lambda" - value = docker_registry_image.this.name + value = var.use_image_tag ? docker_registry_image.this.name : format("%v@%v", docker_registry_image.this.name, docker_registry_image.this.id) +} + +output "image_id" { + description = "The ID of the Docker image" + value = docker_registry_image.this.id } diff --git a/modules/docker-build/variables.tf b/modules/docker-build/variables.tf index a59548a8..509f35f3 100644 --- a/modules/docker-build/variables.tf +++ b/modules/docker-build/variables.tf @@ -10,6 +10,12 @@ variable "create_sam_metadata" { default = false } +variable "use_image_tag" { + description = "Controls whether to use image tag in ECR repository URI or not. Disable this to deploy latest image using ID (sha256:...)" + type = bool + default = true +} + variable "ecr_address" { description = "Address of ECR repository for cross-account container image pulling (optional). Option `create_ecr_repo` must be `false`" type = string @@ -88,3 +94,21 @@ variable "platform" { type = string default = null } + +variable "force_remove" { + description = "Whether to remove image forcibly when the resource is destroyed." + type = bool + default = false +} + +variable "keep_locally" { + description = "Whether to delete the Docker image locally on destroy operation." + type = bool + default = false +} + +variable "triggers" { + description = "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" + type = map(string) + default = {} +} diff --git a/wrappers/docker-build/main.tf b/wrappers/docker-build/main.tf index 81afa562..0375d4eb 100644 --- a/wrappers/docker-build/main.tf +++ b/wrappers/docker-build/main.tf @@ -12,10 +12,14 @@ module "wrapper" { 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) }