From c7de4ad64c4332abcfe0463ee8a9839781c281a2 Mon Sep 17 00:00:00 2001 From: akocbek Date: Thu, 16 Oct 2025 13:38:09 +0100 Subject: [PATCH 1/2] feat: add support to create container registry and secret inside build module --- README.md | 1 + examples/build/README.md | 8 +++++ examples/build/main.tf | 30 +++++++++++++++++++ examples/build/outputs.tf | 24 +++++++++++++++ examples/build/provider.tf | 8 +++++ examples/build/variables.tf | 27 +++++++++++++++++ examples/build/version.tf | 12 ++++++++ main.tf | 39 +++++++++++++----------- modules/build/README.md | 19 ++++++++---- modules/build/main.tf | 51 ++++++++++++++++++++++++++++++-- modules/build/variables.tf | 59 ++++++++++++++++++++++++++++++++----- tests/pr_test.go | 39 +++++++++++++++++++++++- variables.tf | 25 +++++++++------- 13 files changed, 297 insertions(+), 45 deletions(-) create mode 100644 examples/build/README.md create mode 100644 examples/build/main.tf create mode 100644 examples/build/outputs.tf create mode 100644 examples/build/provider.tf create mode 100644 examples/build/variables.tf create mode 100644 examples/build/version.tf diff --git a/README.md b/README.md index ab250073..867c8420 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ This module provisions the IBM Cloud Code Engine fully managed and serverless pl * [secret](./modules/secret) * [Examples](./examples) * [Apps example](./examples/apps) + * [Build example](./examples/build) * [Jobs example](./examples/jobs) * [Contributing](#contributing) diff --git a/examples/build/README.md b/examples/build/README.md new file mode 100644 index 00000000..6064020c --- /dev/null +++ b/examples/build/README.md @@ -0,0 +1,8 @@ +# Build example + +An end-to-end apps example that will provision the following: +- A new resource group if one is not passed in. +- Code Engine project +- Code Engine build +- Code Engine registry secret +- Container registry namespace diff --git a/examples/build/main.tf b/examples/build/main.tf new file mode 100644 index 00000000..1e4666e3 --- /dev/null +++ b/examples/build/main.tf @@ -0,0 +1,30 @@ +######################################################################################################################## +# Resource group +######################################################################################################################## + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.3.0" + # if an existing resource group is not set (null) create a new one using prefix + resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null + existing_resource_group_name = var.resource_group +} + +######################################################################################################################## +# Code Engine instance +######################################################################################################################## + +module "code_engine" { + source = "../.." + ibmcloud_api_key = var.ibmcloud_api_key + resource_group_id = module.resource_group.resource_group_id + project_name = "${var.prefix}-project" + builds = { + "${var.prefix}-build1" = { + source_url = "https://github.com/IBM/CodeEngine" + container_registry_namespace = "cr-ce" + prefix = var.prefix + region = var.region + } + } +} diff --git a/examples/build/outputs.tf b/examples/build/outputs.tf new file mode 100644 index 00000000..7fad9d03 --- /dev/null +++ b/examples/build/outputs.tf @@ -0,0 +1,24 @@ +######################################################################################################################## +# Outputs +######################################################################################################################## + +output "resource_group_id" { + description = "The id of created resource group." + value = module.resource_group.resource_group_id +} + +output "resource_group_name" { + description = "The name of created resource group." + value = module.resource_group.resource_group_name +} + +output "project_id" { + description = "ID of the created code engine project." + value = module.code_engine.project_id +} + +output "build" { + description = "Configuration of the created code engine domain mapping." + value = module.code_engine.build + sensitive = true +} diff --git a/examples/build/provider.tf b/examples/build/provider.tf new file mode 100644 index 00000000..84b69850 --- /dev/null +++ b/examples/build/provider.tf @@ -0,0 +1,8 @@ +######################################################################################################################## +# Provider config +######################################################################################################################## + +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} diff --git a/examples/build/variables.tf b/examples/build/variables.tf new file mode 100644 index 00000000..7fbea71e --- /dev/null +++ b/examples/build/variables.tf @@ -0,0 +1,27 @@ +######################################################################################################################## +# Input variables +######################################################################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API Key" + sensitive = true +} + +variable "region" { + type = string + description = "Region to provision all resources created by this example" + default = "us-south" +} + +variable "prefix" { + type = string + description = "Prefix to append to all resources created by this example" + default = "ce-build" +} + +variable "resource_group" { + type = string + description = "The name of an existing resource group to provision resources in to. If not set a new resource group will be created using the prefix variable" + default = null +} diff --git a/examples/build/version.tf b/examples/build/version.tf new file mode 100644 index 00000000..59c11feb --- /dev/null +++ b/examples/build/version.tf @@ -0,0 +1,12 @@ + +terraform { + required_version = ">= 1.9.0" + # Ensure that there is always 1 example locked into the lowest provider version of the range defined in the main + # module's version.tf (this example), and 1 example that will always use the latest provider version (jobs examples). + required_providers { + ibm = { + source = "IBM-Cloud/ibm" + version = ">= 1.79.0, < 2.0.0" + } + } +} diff --git a/main.tf b/main.tf index 226ed328..f5b783ac 100644 --- a/main.tf +++ b/main.tf @@ -99,24 +99,27 @@ module "secret" { # Code Engine Build ############################################################################## module "build" { - depends_on = [module.secret] - source = "./modules/build" - for_each = var.builds - ibmcloud_api_key = var.ibmcloud_api_key - existing_resource_group_id = var.resource_group_id - project_id = local.project_id - name = each.key - output_image = each.value.output_image - output_secret = each.value.output_secret - source_url = each.value.source_url - strategy_type = each.value.strategy_type - source_context_dir = each.value.source_context_dir - source_revision = each.value.source_revision - source_secret = each.value.source_secret - source_type = each.value.source_type - strategy_size = each.value.strategy_size - strategy_spec_file = each.value.strategy_spec_file - timeout = each.value.timeout + depends_on = [module.secret] + source = "./modules/build" + for_each = var.builds + ibmcloud_api_key = var.ibmcloud_api_key + existing_resource_group_id = var.resource_group_id + project_id = local.project_id + name = each.key + output_image = each.value.output_image + output_secret = each.value.output_secret + source_url = each.value.source_url + strategy_type = each.value.strategy_type + source_context_dir = each.value.source_context_dir + source_revision = each.value.source_revision + source_secret = each.value.source_secret + source_type = each.value.source_type + strategy_size = each.value.strategy_size + strategy_spec_file = each.value.strategy_spec_file + timeout = each.value.timeout + region = each.value.region + container_registry_namespace = each.value.container_registry_namespace + prefix = each.value.prefix } ############################################################################## diff --git a/modules/build/README.md b/modules/build/README.md index cd4ef77e..a8ccd25d 100644 --- a/modules/build/README.md +++ b/modules/build/README.md @@ -40,7 +40,11 @@ You need the following permissions to run this module. ### Modules -No modules. +| Name | Source | Version | +|------|--------|---------| +| [cr\_endpoint](#module\_cr\_endpoint) | terraform-ibm-modules/container-registry/ibm//modules/endpoint | 2.1.0 | +| [cr\_namespace](#module\_cr\_namespace) | terraform-ibm-modules/container-registry/ibm | 2.1.0 | +| [secret](#module\_secret) | ../../modules/secret | n/a | ### Resources @@ -54,21 +58,24 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [container\_registry\_api\_key](#input\_container\_registry\_api\_key) | The API key for the container registry in the target account. This is only used if 'output\_secret' is not set and a new registry secret needs to be created. If not provided, the IBM Cloud API key (ibmcloud\_api\_key) will be used instead. | `string` | `null` | no | +| [container\_registry\_namespace](#input\_container\_registry\_namespace) | The name of the namespace to create in IBM Cloud Container Registry for organizing container images. Must be set if 'output\_image' is not set. | `string` | `null` | no | | [existing\_resource\_group\_id](#input\_existing\_resource\_group\_id) | The ID of an existing resource group where build will be provisioned. This must be the same resource group in which the code engine project was created. | `string` | n/a | yes | | [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | The IBM Cloud API key. | `string` | n/a | yes | | [name](#input\_name) | The name of the build. | `string` | n/a | yes | -| [output\_image](#input\_output\_image) | The name of the image. | `string` | n/a | yes | -| [output\_secret](#input\_output\_secret) | The secret that is required to access the image registry. | `string` | n/a | yes | +| [output\_image](#input\_output\_image) | A container image can be identified by a container image reference with the following structure: registry / namespace / repository:tag. [Learn more](https://cloud.ibm.com/docs/codeengine?topic=codeengine-getting-started).

If not provided, the value will be derived from the 'container\_registry\_namespace' input variable, which must not be null in that case. | `string` | `null` | no | +| [output\_secret](#input\_output\_secret) | The secret that is required to access the IBM Cloud Container Registry. Make sure that the secret is granted with push permissions towards the specified container registry namespace. If not provided, it will be created using the value of 'container\_registry\_api\_key'; if that is not set, 'ibmcloud\_api\_key' will be used instead. | `string` | `null` | no | +| [prefix](#input\_prefix) | Prefix appended to the container registry namespace and registry secret if created. | `string` | `null` | no | | [project\_id](#input\_project\_id) | The ID of the project where build will be created. | `string` | n/a | yes | | [region](#input\_region) | The region in which to provision the build. This must be the same region in which the code engine project was created. | `string` | `"us-south"` | no | | [source\_context\_dir](#input\_source\_context\_dir) | The directory in the repository that contains the buildpacks file or the Dockerfile. | `string` | `null` | no | | [source\_revision](#input\_source\_revision) | Commit, tag, or branch in the source repository to pull. | `string` | `null` | no | -| [source\_secret](#input\_source\_secret) | The name of the secret that is used access the repository source. If the var.source\_type value is `local`, this field must be omitted. | `string` | `null` | no | -| [source\_type](#input\_source\_type) | Specifies the type of source to determine if your build source is in a repository or based on local source code. | `string` | `null` | no | +| [source\_secret](#input\_source\_secret) | The name of the secret that is used access the repository source. If the var.source\_type value is `local`, this input must be omitted. | `string` | `null` | no | +| [source\_type](#input\_source\_type) | Specifies the type of source to determine if your build source is in a repository or based on local source code. If the value is `local`, then 'source\_secret' input must be omitted. | `string` | `null` | no | | [source\_url](#input\_source\_url) | The URL of the code repository. | `string` | n/a | yes | | [strategy\_size](#input\_strategy\_size) | The size for the build, which determines the amount of resources used. | `string` | `null` | no | | [strategy\_spec\_file](#input\_strategy\_spec\_file) | The path to the specification file that is used for build strategies for building an image. | `string` | `null` | no | -| [strategy\_type](#input\_strategy\_type) | The strategy to use for building the image. | `string` | n/a | yes | +| [strategy\_type](#input\_strategy\_type) | The strategy to use for building the image. | `string` | `"dockerfile"` | no | | [timeout](#input\_timeout) | The maximum amount of time, in seconds, that can pass before the build must succeed or fail. | `number` | `600` | no | ### Outputs diff --git a/modules/build/main.tf b/modules/build/main.tf index 0efb886a..814042b7 100644 --- a/modules/build/main.tf +++ b/modules/build/main.tf @@ -4,11 +4,15 @@ # Create Code Engine build ############################################################################## +locals { + prefix = var.prefix != null ? (trimspace(var.prefix) != "" ? "${var.prefix}-" : "") : "" +} + resource "ibm_code_engine_build" "ce_build" { project_id = var.project_id name = var.name - output_image = var.output_image - output_secret = var.output_secret + output_image = local.output_image + output_secret = var.output_secret != null ? var.output_secret : module.secret[0].name source_url = var.source_url source_context_dir = var.source_context_dir source_revision = var.source_revision @@ -39,3 +43,46 @@ resource "terraform_data" "run_build" { } } } + + +############################################################################## +# Container Registry +############################################################################## + +locals { + create_cr_namespace = var.output_image == null && var.container_registry_namespace != null ? true : false + image_container = local.create_cr_namespace ? "${module.cr_endpoint[0].container_registry_endpoint_private}/${module.cr_namespace[0].namespace_name}" : null + output_image = local.create_cr_namespace ? "${local.image_container}/${var.name}" : var.output_image +} + +module "cr_namespace" { + count = local.create_cr_namespace ? 1 : 0 + source = "terraform-ibm-modules/container-registry/ibm" + version = "2.1.0" + namespace_name = "${local.prefix}${var.container_registry_namespace}" + resource_group_id = var.existing_resource_group_id +} + +module "cr_endpoint" { + count = local.create_cr_namespace ? 1 : 0 + source = "terraform-ibm-modules/container-registry/ibm//modules/endpoint" + version = "2.1.0" + region = var.region +} + +############################################################################## +# Code Engine Secret +############################################################################## + +module "secret" { + count = var.output_secret == null ? 1 : 0 + source = "../../modules/secret" + project_id = var.project_id + name = "${local.prefix}registry-access-secret" + data = { + password = var.container_registry_api_key != null ? var.container_registry_api_key : var.ibmcloud_api_key, + username = "iamapikey", + server = module.cr_endpoint[0].container_registry_endpoint_private + } + format = "registry" +} diff --git a/modules/build/variables.tf b/modules/build/variables.tf index c4828f17..fb58eb5b 100644 --- a/modules/build/variables.tf +++ b/modules/build/variables.tf @@ -8,6 +8,12 @@ variable "ibmcloud_api_key" { sensitive = true } +variable "prefix" { + type = string + description = "Prefix appended to the container registry namespace and registry secret if created." + default = null +} + variable "existing_resource_group_id" { description = "The ID of an existing resource group where build will be provisioned. This must be the same resource group in which the code engine project was created." type = string @@ -24,18 +30,19 @@ variable "name" { } variable "output_image" { - description = "The name of the image." - type = string -} + description = < Date: Mon, 3 Nov 2025 15:43:51 +0000 Subject: [PATCH 2/2] update merge conflicts --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 867c8420..7391f6d5 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ No resources. |------|-------------|------|---------|:--------:| | [apps](#input\_apps) | A map of code engine apps to be created. |
map(object({
image_reference = string
image_secret = optional(string)
run_env_variables = optional(list(object({
type = optional(string)
name = optional(string)
value = optional(string)
prefix = optional(string)
key = optional(string)
reference = optional(string)
})))
run_volume_mounts = optional(list(object({
mount_path = string
reference = string
name = optional(string)
type = string
})))
image_port = optional(number)
managed_domain_mappings = optional(string)
run_arguments = optional(list(string))
run_as_user = optional(number)
run_commands = optional(list(string))
run_service_account = optional(string)
scale_concurrency = optional(number)
scale_concurrency_target = optional(number)
scale_cpu_limit = optional(string)
scale_ephemeral_storage_limit = optional(string)
scale_initial_instances = optional(number)
scale_max_instances = optional(number)
scale_memory_limit = optional(string)
scale_min_instances = optional(number)
scale_request_timeout = optional(number)
scale_down_delay = optional(number)
}))
| `{}` | no | | [bindings](#input\_bindings) | A map of code engine bindings to be created. |
map(object({
secret_name = string
components = list(object({
name = string
resource_type = string
}))
}))
| `{}` | no | -| [builds](#input\_builds) | A map of code engine builds to be created. Requires 'ibmcloud\_api\_key' to be set for authentication and execution. |
map(object({
output_image = string
output_secret = string # pragma: allowlist secret
source_url = string
strategy_type = string
source_context_dir = optional(string)
source_revision = optional(string)
source_secret = optional(string)
source_type = optional(string)
strategy_size = optional(string)
strategy_spec_file = optional(string)
timeout = optional(number)
}))
| `{}` | no | +| [builds](#input\_builds) | A map of code engine builds to be created. Requires 'ibmcloud\_api\_key' to be set for authentication and execution. |
map(object({
output_image = optional(string)
output_secret = optional(string) # pragma: allowlist secret
source_url = string
strategy_type = optional(string)
source_context_dir = optional(string)
source_revision = optional(string)
source_secret = optional(string)
source_type = optional(string)
strategy_size = optional(string)
strategy_spec_file = optional(string)
timeout = optional(number)
region = optional(string)
container_registry_namespace = optional(string)
prefix = optional(string)
}))
| `{}` | no | | [cbr\_rules](#input\_cbr\_rules) | The context-based restrictions rule to create. Only one rule is allowed. |
list(object({
description = string
account_id = string
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
operations = optional(list(object({
api_types = list(object({
api_type_id = string
}))
})))
}))
| `[]` | no | | [config\_maps](#input\_config\_maps) | A map of code engine config maps to be created. |
map(object({
data = map(string)
}))
| `{}` | no | | [domain\_mappings](#input\_domain\_mappings) | A map of code engine domain mappings to be created. |
map(object({
tls_secret = string # pragma: allowlist secret
components = list(object({
name = string
resource_type = string
}))
}))
| `{}` | no |