Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ Optionally, you need the following permissions to attach Access Management tags
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.9.0 |
| <a name="requirement_external"></a> [external](#requirement\_external) | ~> 2 |
| <a name="requirement_ibm"></a> [ibm](#requirement\_ibm) | >= 1.78.2, < 2.0.0 |
| <a name="requirement_kubernetes"></a> [kubernetes](#requirement\_kubernetes) | >= 2.16.1, < 3.0.0 |
| <a name="requirement_null"></a> [null](#requirement\_null) | >= 3.2.1, < 4.0.0 |
Expand Down Expand Up @@ -327,9 +328,11 @@ Optionally, you need the following permissions to attach Access Management tags
| [null_resource.install_required_binaries](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [null_resource.ocp_console_management](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [time_sleep.wait_for_auth_policy](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource |
| [external_external.ocp_addon_versions](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source |
| [ibm_container_addons.existing_addons](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/data-sources/container_addons) | data source |
| [ibm_container_cluster_config.cluster_config](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/data-sources/container_cluster_config) | data source |
| [ibm_container_cluster_versions.cluster_versions](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/data-sources/container_cluster_versions) | data source |
| [ibm_iam_auth_token.tokendata](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/data-sources/iam_auth_token) | data source |
| [ibm_is_lbs.all_lbs](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/data-sources/is_lbs) | data source |
| [ibm_is_virtual_endpoint_gateway.api_vpe](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/data-sources/is_virtual_endpoint_gateway) | data source |
| [ibm_is_virtual_endpoint_gateway.master_vpe](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/data-sources/is_virtual_endpoint_gateway) | data source |
Expand Down
15 changes: 15 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ locals {
binaries_path = "/tmp"
}

########################################################################################################################
# Get OCP AI Add-on Versions
########################################################################################################################

data "ibm_iam_auth_token" "tokendata" {}

data "external" "ocp_addon_versions" {
program = ["python3", "${path.module}/scripts/get_ocp_addon_versions.py"]
query = {
IAM_TOKEN = sensitive(data.ibm_iam_auth_token.tokendata.iam_access_token)
REGION = var.region
}
}

# Local block to verify validations for OCP AI Addon.
locals {

Expand All @@ -65,6 +79,7 @@ locals {
is_gpu = contains(["gx2", "gx3", "gx4"], split(".", pool.machine_type)[0])
}
}
ocp_ai_addon_supported_versions = jsondecode(data.external.ocp_addon_versions.result["openshift-ai"])
}

# Separate local block to handle os validations
Expand Down
132 changes: 132 additions & 0 deletions scripts/get_ocp_addon_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/env python3
import http.client
import json
import sys


def parse_input():
"""
Reads JSON input from stdin and parses it into a dictionary.
Returns:
dict: Parsed input data.
"""
try:
data = json.loads(sys.stdin.read())
except json.JSONDecodeError as e:
raise ValueError("Invalid JSON input") from e
return data


def validate_inputs(data):
"""
Validates required inputs 'IAM_TOKEN' and 'REGION' from the parsed input.
Args:
data (dict): Input data parsed from JSON.
Returns:
tuple: A tuple containing (IAM_TOKEN, REGION).
"""
token = data.get("IAM_TOKEN")
if not token:
raise ValueError("IAM_TOKEN is required")

region = data.get("REGION")
if not region:
raise ValueError("REGION is required")

return token, region


def fetch_addon_versions(iam_token, region):
"""
Fetches openshift add-on versions using HTTP connection.
Args:
iam_token (str): IBM Cloud IAM token for authentication.
region (str): Region to query for add-ons.
Returns:
list: Parsed JSON response containing add-on information.
"""
url = "/global/v1/addons"
host = "containers.cloud.ibm.com"
headers = {
"Authorization": f"Bearer {iam_token}",
"Accept": "application/json",
"X-Region": region,
}

conn = http.client.HTTPSConnection(host)
try:
conn.request("GET", url, headers=headers)
response = conn.getresponse()
data = response.read().decode()

if response.status != 200:
raise RuntimeError(
f"API request failed: {response.status} {response.reason} - {data}"
)

return json.loads(data)
except http.client.HTTPException as e:
raise RuntimeError("HTTP request failed") from e
finally:
conn.close()


def transform_addons(addons_data):
"""
Transforms the raw add-on data into a nested dictionary structured by add-on name and version.
Args:
addons_data: Raw data returned by the add-on API.
Returns:
dict: Transformed add-on data suitable for Terraform consumption.
"""
result = {}

for addon in addons_data:
name = addon.get("name")
version = addon.get("version")

supported_ocp = addon.get("supportedOCPRange", "unsupported")
supported_kube = addon.get("supportedKubeRange", "unsupported")

if name not in result:
result[name] = {}

result[name][version] = {
"supported_openshift_range": supported_ocp,
"supported_kubernetes_range": supported_kube,
}

if not result:
raise RuntimeError("No add-on data found.")

return result


def format_for_terraform(result):
"""
Converts the transformed add-on data into JSON strings for Terraform external data source consumption.
Args:
result (dict): Transformed add-on data.
Returns:
dict: A dictionary mapping add-on names to JSON strings of their version info.
"""
return {name: json.dumps(versions) for name, versions in result.items()}


def main():
"""
Main execution function: reads input, validates, fetches API data, transforms it,
formats it for Terraform and prints the JSON output.
"""
data = parse_input()
iam_token, region = validate_inputs(data)

addons_data = fetch_addon_versions(iam_token, region)
transformed = transform_addons(addons_data)
output = format_for_terraform(transformed)

print(json.dumps(output))


if __name__ == "__main__":
main()
25 changes: 23 additions & 2 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,30 @@ variable "addons" {
nullable = false
default = {}

########################################################################################################################
# OCP AI Addon version validation
########################################################################################################################

validation {
condition = (lookup(var.addons, "openshift-ai", null) != null ? lookup(var.addons["openshift-ai"], "version", null) == null : true) || (tonumber(local.ocp_version_num) >= 4.16)
error_message = "OCP AI add-on requires OCP version >= 4.16.0"
condition = (
lookup(var.addons, "openshift-ai", null) == null ||
lookup(var.addons["openshift-ai"], "version", null) == null ||
(
contains(keys(local.ocp_ai_addon_supported_versions), var.addons["openshift-ai"].version) &&
(
local.ocp_version_num >= tonumber(regexall("\\d+\\.\\d+", split(" ", local.ocp_ai_addon_supported_versions[var.addons["openshift-ai"].version].supported_openshift_range)[0])[0])) &&
(
local.ocp_version_num < tonumber(regexall("\\d+\\.\\d+", split(" ", local.ocp_ai_addon_supported_versions[var.addons["openshift-ai"].version].supported_openshift_range)[1])[0])
)
)
)

error_message = (
var.addons["openshift-ai"] != null && var.addons["openshift-ai"].version != null ?
(contains(keys(local.ocp_ai_addon_supported_versions), var.addons["openshift-ai"].version) ?
format("OCP AI add-on version %s requires OCP version %s", var.addons["openshift-ai"].version, local.ocp_ai_addon_supported_versions[var.addons["openshift-ai"].version].supported_openshift_range) :
format("OCP AI add-on version %s is not supported.", var.addons["openshift-ai"].version)) : null
)
}

validation {
Expand Down
4 changes: 4 additions & 0 deletions version.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,9 @@ terraform {
source = "hashicorp/time"
version = ">= 0.9.1, < 1.0.0"
}
external = {
source = "hashicorp/external"
version = "~> 2"
}
}
}