Skip to content

Commit 4994d03

Browse files
authored
feat: add a new feature to run image builds (#201)
1 parent f99d24a commit 4994d03

File tree

10 files changed

+352
-89
lines changed

10 files changed

+352
-89
lines changed

ibm_catalog.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,9 @@
352352
{
353353
"key": "builds"
354354
},
355+
{
356+
"key": "container_registry_namespace"
357+
},
355358
{
356359
"key": "domain_mappings"
357360
},

modules/build/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,6 @@ No modules.
7373
| <a name="output_build_id"></a> [build\_id](#output\_build\_id) | The ID of the created code engine build. |
7474
| <a name="output_id"></a> [id](#output\_id) | The unique identifier of the created code engine build. |
7575
| <a name="output_name"></a> [name](#output\_name) | The name of the created code engine build. |
76+
| <a name="output_output_image"></a> [output\_image](#output\_output\_image) | The container image reference of the created code engine build. |
77+
| <a name="output_output_secret"></a> [output\_secret](#output\_output\_secret) | The registry secret of the created code engine build. |
7678
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

modules/build/outputs.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,13 @@ output "name" {
1616
description = "The name of the created code engine build."
1717
value = resource.ibm_code_engine_build.ce_build.name
1818
}
19+
20+
output "output_image" {
21+
description = "The container image reference of the created code engine build."
22+
value = resource.ibm_code_engine_build.ce_build.output_image
23+
}
24+
25+
output "output_secret" {
26+
description = "The registry secret of the created code engine build."
27+
value = resource.ibm_code_engine_build.ce_build.output_secret
28+
}

solutions/project/DA-inputs.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ The `builds` input variable allows you to provide details of the of builds which
1717

1818
### Options for Builds
1919

20-
- `output_image` (required): The name of the image.
20+
- `output_image` (optional): The name of the image. A container image can be identified by a container image reference with the following structure: registry / namespace / repository : tag. If not provided, the name is automatically build using region registry / container_registry_namespace input / build name.
2121
- `output_secret` (required): The secret that is required to access the image registry.
2222
- `source_url` (required): The URL of the code repository.
2323
- `strategy_type` (required): Specifies the type of source to determine if your build source is in a repository or based on local source code.
@@ -118,10 +118,30 @@ The `secrets` input variable allows you to provide a method to include sensitive
118118
### Example for Secrets
119119

120120
```hcl
121+
# generic secret
121122
{
122123
"your-secret-name" = {
123124
format = "generic"
124125
data = { "key_1" : "value_1", "key_2" : "value_2" }
125126
}
126127
}
128+
129+
# registry secret
130+
"registry_secret_name" = {
131+
format = "registry"
132+
optional("data") = {
133+
"server" = "private.us.icr.io",
134+
"username" = "iamapikey",
135+
"password" = iam_api_key, # pragma: allowlist secret
136+
}
137+
}
138+
139+
# private repository
140+
"private_repo" = {
141+
format = "generic"
142+
"data" = {
143+
"password" = github_token, # pragma: allowlist secret
144+
"username" = github_user
145+
}
146+
}
127147
```

solutions/project/main.tf

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,54 @@ module "project" {
2626
##############################################################################
2727
# Code Engine Build
2828
##############################################################################
29+
locals {
30+
registry_region_result = data.external.container_registry_region.result
31+
registry = lookup(local.registry_region_result, "registry", null)
32+
container_registry = local.registry != null ? "private.${local.registry}" : null
33+
registry_region_error = lookup(local.registry_region_result, "error", null)
34+
35+
# This will cause Terraform to fail if "error" is present in the external script output executed as a part of container_registry_region
36+
# tflint-ignore: terraform_unused_declarations
37+
fail_if_registry_region_error = local.registry_region_error != null ? tobool("Registry region script failed: ${local.registry_region_error}") : null
38+
39+
# if no build defines a container image reference (output_image), a new container registry namespace must be created using container_registry_namespace.
40+
any_missing_output_image = anytrue([
41+
for build in values(var.builds) :
42+
!contains(keys(build), "output_image") || build.output_image == null
43+
])
44+
image_container = local.any_missing_output_image && local.container_registry != null ? "${local.container_registry}/${resource.ibm_cr_namespace.my_namespace[0].name}" : ""
45+
46+
# if output_image not exists then a new created container image reference
47+
updated_builds = {
48+
for name, build in var.builds :
49+
name => merge(
50+
build,
51+
{
52+
output_image = coalesce(build.output_image, "${local.image_container}/${name}")
53+
}
54+
)
55+
}
56+
}
57+
58+
resource "ibm_cr_namespace" "my_namespace" {
59+
count = local.any_missing_output_image && var.container_registry_namespace != null ? 1 : 0
60+
name = var.container_registry_namespace
61+
}
62+
63+
data "external" "container_registry_region" {
64+
program = ["bash", "${path.module}/scripts/get-cr-region.sh"]
65+
66+
query = {
67+
RESOURCE_GROUP_ID = module.resource_group.resource_group_id
68+
REGION = var.region
69+
IBMCLOUD_API_KEY = var.ibmcloud_api_key
70+
}
71+
}
72+
2973
module "build" {
3074
depends_on = [module.secret]
3175
source = "../../modules/build"
32-
for_each = var.builds
76+
for_each = local.updated_builds
3377
project_id = module.project.project_id
3478
name = each.key
3579
output_image = each.value.output_image
@@ -43,7 +87,21 @@ module "build" {
4387
strategy_size = each.value.strategy_size
4488
strategy_spec_file = each.value.strategy_spec_file
4589
timeout = each.value.timeout
90+
}
4691

92+
resource "null_resource" "run_build" {
93+
depends_on = [module.build]
94+
provisioner "local-exec" {
95+
interpreter = ["/bin/bash", "-c"]
96+
command = "${path.module}/scripts/build-run.sh"
97+
environment = {
98+
IBMCLOUD_API_KEY = var.ibmcloud_api_key
99+
RESOURCE_GROUP_ID = module.resource_group.resource_group_id
100+
CE_PROJECT_NAME = module.project.name
101+
REGION = var.region
102+
BUILDS = join(" ", keys(local.updated_builds))
103+
}
104+
}
47105
}
48106

49107
##############################################################################
@@ -72,9 +130,30 @@ module "config_map" {
72130
##############################################################################
73131
# Code Engine Secret
74132
##############################################################################
133+
locals {
134+
# if the secret is a registry type, inject generated credentials (username, password, server) if they're not already provided.
135+
secrets = {
136+
for name, secret in var.secrets :
137+
name => merge(
138+
secret,
139+
{
140+
data = (
141+
secret.format == "registry"
142+
? merge(secret.data, {
143+
password = lookup(secret.data, "password", var.ibmcloud_api_key),
144+
username = lookup(secret.data, "username", "iamapikey"),
145+
server = lookup(secret.data, "server", local.container_registry)
146+
})
147+
: secret.data
148+
)
149+
}
150+
)
151+
}
152+
}
153+
75154
module "secret" {
76155
source = "../../modules/secret"
77-
for_each = var.secrets
156+
for_each = local.secrets
78157
project_id = module.project.project_id
79158
name = each.key
80159
data = sensitive(each.value.data)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
set -e
3+
4+
if [[ -z "${IBMCLOUD_API_KEY}" ]]; then
5+
echo "IBMCLOUD_API_KEY is required" >&2
6+
exit 1
7+
fi
8+
9+
if [[ -z "${RESOURCE_GROUP_ID}" ]]; then
10+
echo "RESOURCE_GROUP_ID is required" >&2
11+
exit 1
12+
fi
13+
14+
if [[ -z "${CE_PROJECT_NAME}" ]]; then
15+
echo "CE_PROJECT_NAME is required" >&2
16+
exit 1
17+
fi
18+
19+
if [[ -z "${REGION}" ]]; then
20+
echo "REGION is required" >&2
21+
exit 1
22+
fi
23+
24+
if [[ -z "${BUILDS}" ]]; then
25+
echo "BUILDS is required" >&2
26+
exit 1
27+
fi
28+
29+
ibmcloud login -r "${REGION}" -g "${RESOURCE_GROUP_ID}" --quiet
30+
31+
# selecet the right code engine project
32+
ibmcloud ce project select -n "${CE_PROJECT_NAME}"
33+
34+
# run a build for all builds
35+
for build in $BUILDS; do
36+
echo "$build"
37+
ibmcloud ce buildrun submit --build "$build"
38+
done
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
INPUT=$(cat)
5+
REGION=$(echo "$INPUT" | jq -r '.REGION')
6+
RESOURCE_GROUP_ID=$(echo "$INPUT" | jq -r '.RESOURCE_GROUP_ID')
7+
IBMCLOUD_API_KEY=$(echo "$INPUT" | jq -r '.IBMCLOUD_API_KEY')
8+
export IBMCLOUD_API_KEY
9+
10+
if [[ -z "${IBMCLOUD_API_KEY}" || "${IBMCLOUD_API_KEY}" == "null" ]]; then
11+
echo '{"error": "IBMCLOUD_API_KEY is required"}'
12+
exit 0
13+
fi
14+
15+
if [[ -z "${RESOURCE_GROUP_ID}" || "${RESOURCE_GROUP_ID}" == "null" ]]; then
16+
echo '{"error": "RESOURCE_GROUP_ID is required"}'
17+
exit 0
18+
fi
19+
20+
if [[ -z "${REGION}" || "${REGION}" == "null" ]]; then
21+
echo '{"error": "REGION is required"}'
22+
exit 0
23+
fi
24+
25+
if ! ibmcloud login -r "${REGION}" -g "${RESOURCE_GROUP_ID}" --quiet > /dev/null 2>&1; then
26+
printf '{"error": "Failed to login using: ibmcloud login -r %s -g %s"}' "$REGION" "$RESOURCE_GROUP_ID"
27+
exit 0
28+
fi
29+
30+
# extract registry value from text "You are targeting region 'us-south', the registry is 'us.icr.io'."
31+
registry=$(ibmcloud cr region 2>/dev/null | grep registry | sed -E "s/.*registry is '([^']+)'.*/\1/")
32+
33+
# Validate registry value
34+
if [[ -z "$registry" ]]; then
35+
echo '{"error": "Failed to parse registry region from ibmcloud cr region"}'
36+
exit 0
37+
fi
38+
39+
echo "{\"registry\": \"${registry}\"}"

solutions/project/variables.tf

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ variable "project_name" {
4646
variable "builds" {
4747
description = "A map of code engine builds to be created.[Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-code-engine/blob/main/solutions/project/DA-inputs.md#builds)"
4848
type = map(object({
49-
output_image = string
49+
output_image = optional(string)
5050
output_secret = string # pragma: allowlist secret
5151
source_url = string
5252
strategy_type = string
@@ -61,6 +61,20 @@ variable "builds" {
6161
default = {}
6262
}
6363

64+
variable "container_registry_namespace" {
65+
description = "The name of the namespace to create in IBM Cloud Container Registry for organizing container images. Used only for builds that do not have output_image set."
66+
type = string
67+
default = null
68+
69+
validation {
70+
condition = alltrue([
71+
for build in values(var.builds) :
72+
contains(keys(build), "output_image") && build.output_image != null
73+
]) || var.container_registry_namespace != null
74+
error_message = "container_registry_namespace is required because at least one build is missing an output_image"
75+
}
76+
}
77+
6478
##############################################################################
6579
# Code Engine Domain Mapping
6680
##############################################################################

solutions/project/version.tf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,13 @@ terraform {
66
source = "IBM-Cloud/ibm"
77
version = "1.79.2"
88
}
9+
external = {
10+
source = "hashicorp/external"
11+
version = "2.3.5"
12+
}
13+
null = {
14+
source = "hashicorp/null"
15+
version = "3.2.4"
16+
}
917
}
1018
}

0 commit comments

Comments
 (0)