diff --git a/compute/lambda/rvn-lambda-definition.yml b/compute/lambda/rvn-lambda-definition.yml index efd17d14..9fe632c1 100644 --- a/compute/lambda/rvn-lambda-definition.yml +++ b/compute/lambda/rvn-lambda-definition.yml @@ -3,8 +3,11 @@ definition: name: Lambda Function description: AWS Lambda function with zip or container image deployments, alias-based releases, IAM, logs, and optional function URLs. release: - version: 0.3.3 - description: Document cross-account ECR support for image registry deploys. + version: 0.4.0 + description: >- + Stop creating a module-owned ECR repository when pulling from an image + registry. Add a required initial image tag or digest used to create the + function before its first deployment. module: inputs: - $include: ../../partials/inputs/aws-account.yml @@ -209,6 +212,15 @@ module: show_when: package_type: Image build_source: image_registry + - id: initial_image_ref + label: Initial image tag or digest + type: string + description: Tag or digest used only to create the function before its first deployment. It must already exist in the image repository above. Deployments promote the tag or digest you pass at deploy time; changing this value afterward has no effect. Do not include the repository URI. + placeholder: sha256:... or latest + required: true + show_when: + package_type: Image + build_source: image_registry - id: image_start_command label: Start command type: string_array @@ -895,6 +907,9 @@ module: show_when: lambda_type: regional package_type: Image + build_source: + - dockerfile + - nixpacks - id: ecr_scan_on_push_enabled label: Scan images on push type: boolean @@ -904,6 +919,9 @@ module: show_when: lambda_type: regional package_type: Image + build_source: + - dockerfile + - nixpacks - id: ecr_force_deletion_enabled label: Force delete image repository type: boolean @@ -913,6 +931,9 @@ module: show_when: lambda_type: regional package_type: Image + build_source: + - dockerfile + - nixpacks - $include: ../../partials/inputs/misc-section.yml - $include: ../../partials/inputs/tags.yml - $include: ../../partials/inputs/terraform-settings.yml @@ -943,7 +964,7 @@ module: ecr_default_lifecycle_policy_enabled: true ecr_force_deletion_enabled: << module.input.ecr_force_deletion_enabled >> ecr_repository_creation_enabled: >- - << module.input.lambda_type != "edge" && module.input.package_type == "Image" >> + << module.input.lambda_type != "edge" && module.input.package_type == "Image" && module.input.build_source != "image_registry" >> ecr_repository_name: << module.input.name >> ecr_scan_on_push_enabled: << module.input.ecr_scan_on_push_enabled >> environment_variables: >- @@ -974,6 +995,10 @@ module: (len(module.input.image_start_command || []) > 0 ? module.input.image_start_command : nil), entry_point: (len(module.input.image_entry_point || []) > 0 ? module.input.image_entry_point : nil), working_directory: module.input.image_working_directory} : nil) >> + image_uri: >- + << module.input.lambda_type != "edge" && module.input.package_type == "Image" && module.input.build_source == "image_registry" ? + (module.input.initial_image_ref contains "sha256:" ? module.input.image_repository + "@" + module.input.initial_image_ref : + module.input.image_repository + ":" + module.input.initial_image_ref) : nil >> kms_key_arn: >- << module.input.lambda_type == "edge" ? nil : (module.input.kms_key_arn || nil) >> lambda_at_edge_enabled: << module.input.lambda_type == "edge" >> diff --git a/compute/lambda/tests/basic.tftest.hcl b/compute/lambda/tests/basic.tftest.hcl index 75ea53ce..ca313362 100644 --- a/compute/lambda/tests/basic.tftest.hcl +++ b/compute/lambda/tests/basic.tftest.hcl @@ -155,6 +155,40 @@ run "permissions_and_event_source_mappings" { } } +run "image_registry_no_module_ecr" { + command = plan + + variables { + package_type = "Image" + runtime = null + handler = null + s3_bucket = null + s3_key = null + ecr_repository_creation_enabled = false + image_uri = "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-function:v1" + } + + assert { + condition = aws_lambda_function.this.package_type == "Image" + error_message = "Package type should be Image." + } + + assert { + condition = aws_lambda_function.this.image_uri == "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-function:v1" + error_message = "Function should be created from the external image URI." + } + + assert { + condition = length(module.ecr) == 0 + error_message = "No module-owned ECR repository should be created for external image registries." + } + + assert { + condition = length(terraform_data.bootstrap_image) == 0 + error_message = "No bootstrap image should be seeded when the image comes from an external registry." + } +} + run "aliases_created" { command = plan diff --git a/tools/ravion-modules/test/compiler.test.ts b/tools/ravion-modules/test/compiler.test.ts index e89094be..55822d7f 100644 --- a/tools/ravion-modules/test/compiler.test.ts +++ b/tools/ravion-modules/test/compiler.test.ts @@ -167,6 +167,31 @@ describe("compiler", () => { ); }); + it("gates the Lambda ECR repository on build source and seeds image-registry creates from an initial ref", async () => { + const compiled = await compileDefinitionFile(join(repoRoot, "compute", "lambda", "rvn-lambda-definition.yml")); + const inputs = getModuleInputs(compiled.module); + + const initialImageRef = findInput(inputs, "initial_image_ref"); + assert.equal(initialImageRef.label, "Initial image tag or digest"); + assert.equal(initialImageRef.required, true); + assert.equal(getBuildSourceShowWhen(initialImageRef), "image_registry"); + + assert.deepEqual(getBuildSourceShowWhen(findInput(inputs, "section_ecr")), ["dockerfile", "nixpacks"]); + assert.deepEqual(getBuildSourceShowWhen(findInput(inputs, "ecr_scan_on_push_enabled")), ["dockerfile", "nixpacks"]); + assert.deepEqual(getBuildSourceShowWhen(findInput(inputs, "ecr_force_deletion_enabled")), ["dockerfile", "nixpacks"]); + + assert.equal( + getTerraformVariable(compiled.module, "ecr_repository_creation_enabled"), + '<< module.input.lambda_type != "edge" && module.input.package_type == "Image" && module.input.build_source != "image_registry" >>', + ); + + const imageUri = assertString(getTerraformVariable(compiled.module, "image_uri")); + assert.match(imageUri, /module\.input\.build_source == "image_registry"/); + assert.match(imageUri, /module\.input\.initial_image_ref contains "sha256:"/); + assert.match(imageUri, /module\.input\.image_repository \+ "@" \+ module\.input\.initial_image_ref/); + assert.match(imageUri, /module\.input\.image_repository \+ ":" \+ module\.input\.initial_image_ref/); + }); + it("compiles Railpack inputs and builder object for static builds", async () => { const compiled = await compileDefinitionFile(join(repoRoot, "hosting", "static_site", "rvn-aws-static-definition.yml")); const inputs = getModuleInputs(compiled.module);