diff --git a/README.md b/README.md index a456ee54..0732f25b 100644 --- a/README.md +++ b/README.md @@ -253,6 +253,7 @@ Optionally, you need the following permissions to attach Access Management tags | [cbr\_rule](#module\_cbr\_rule) | terraform-ibm-modules/cbr/ibm//modules/cbr-rule-module | 1.32.6 | | [cos\_instance](#module\_cos\_instance) | terraform-ibm-modules/cos/ibm | 8.21.25 | | [existing\_secrets\_manager\_instance\_parser](#module\_existing\_secrets\_manager\_instance\_parser) | terraform-ibm-modules/common-utilities/ibm//modules/crn-parser | 1.2.0 | +| [get\_ocp\_version](#module\_get\_ocp\_version) | ./modules/get-ocp-version | n/a | ### Resources @@ -293,6 +294,7 @@ Optionally, you need the following permissions to attach Access Management tags | [additional\_vpe\_security\_group\_ids](#input\_additional\_vpe\_security\_group\_ids) | Additional security groups to add to all existing load balancers. This comes in addition to the IBM maintained security group. |
object({
master = optional(list(string), [])
registry = optional(list(string), [])
api = optional(list(string), [])
})
| `{}` | no | | [addons](#input\_addons) | Map of OCP cluster add-on versions to install (NOTE: The 'vpc-block-csi-driver' add-on is installed by default for VPC clusters and 'ibm-storage-operator' is installed by default in OCP 4.15 and later, however you can explicitly specify it here if you wish to choose a later version than the default one). For full list of all supported add-ons and versions, see https://cloud.ibm.com/docs/containers?topic=containers-supported-cluster-addon-versions |
object({
debug-tool = optional(object({
version = optional(string)
parameters_json = optional(string)
}))
image-key-synchronizer = optional(object({
version = optional(string)
parameters_json = optional(string)
}))
openshift-data-foundation = optional(object({
version = optional(string)
parameters_json = optional(string)
}))
vpc-file-csi-driver = optional(object({
version = optional(string)
parameters_json = optional(string)
}))
static-route = optional(object({
version = optional(string)
parameters_json = optional(string)
}))
cluster-autoscaler = optional(object({
version = optional(string)
parameters_json = optional(string)
}))
vpc-block-csi-driver = optional(object({
version = optional(string)
parameters_json = optional(string)
}))
ibm-storage-operator = optional(object({
version = optional(string)
parameters_json = optional(string)
}))
openshift-ai = optional(object({
version = optional(string)
parameters_json = optional(string)
}))
})
| `{}` | no | | [allow\_default\_worker\_pool\_replacement](#input\_allow\_default\_worker\_pool\_replacement) | (Advanced users) Set to true to allow the module to recreate a default worker pool. If you wish to make any change to the default worker pool which requires the re-creation of the default pool follow these [steps](https://github.com/terraform-ibm-modules/terraform-ibm-base-ocp-vpc?tab=readme-ov-file#important-considerations-for-terraform-and-default-worker-pool). | `bool` | `false` | no | +| [allow\_kube\_version\_upgrade](#input\_allow\_kube\_version\_upgrade) | Set to true to allow the module to upgrade the kube version of the cluster. If you wish to make any change to the kube version, set this variable to true. | `bool` | `false` | no | | [attach\_ibm\_managed\_security\_group](#input\_attach\_ibm\_managed\_security\_group) | Specify whether to attach the IBM-defined default security group (whose name is kube-) to all worker nodes. Only applicable if `custom_security_group_ids` is set. | `bool` | `true` | no | | [cbr\_rules](#input\_cbr\_rules) | The list of context-based restriction rules to create. |
list(object({
description = string
account_id = string
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
tags = optional(list(object({
name = string
value = string
})), [])
operations = optional(list(object({
api_types = list(object({
api_type_id = string
}))
})))
}))
| `[]` | no | | [cluster\_config\_endpoint\_type](#input\_cluster\_config\_endpoint\_type) | Specify which type of endpoint to use for cluster config access: 'default', 'private', 'vpe', 'link'. A 'default' value uses the default endpoint of the cluster. | `string` | `"default"` | no | @@ -308,6 +310,7 @@ Optionally, you need the following permissions to attach Access Management tags | [existing\_cos\_id](#input\_existing\_cos\_id) | The COS id of an already existing COS instance to use for OpenShift internal registry storage. Only required if 'enable\_registry\_storage' and 'use\_existing\_cos' are true. | `string` | `null` | no | | [existing\_secrets\_manager\_instance\_crn](#input\_existing\_secrets\_manager\_instance\_crn) | CRN of the Secrets Manager instance where Ingress certificate secrets are stored. If 'enable\_secrets\_manager\_integration' is set to true then this value is required. | `string` | `null` | no | | [force\_delete\_storage](#input\_force\_delete\_storage) | Flag indicating whether or not to delete attached storage when destroying the cluster - Default: false | `bool` | `false` | no | +| [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | APIkey that's associated with the account to use, set via environment variable TF\_VAR\_ibmcloud\_api\_key | `string` | `null` | no | | [ignore\_worker\_pool\_size\_changes](#input\_ignore\_worker\_pool\_size\_changes) | Enable if using worker autoscaling. Stops Terraform managing worker count | `bool` | `false` | no | | [kms\_config](#input\_kms\_config) | Use to attach a KMS instance to the cluster. If account\_id is not provided, defaults to the account in use. |
object({
crk_id = string
instance_id = string
private_endpoint = optional(bool, true) # defaults to true
account_id = optional(string) # To attach KMS instance from another account
wait_for_apply = optional(bool, true) # defaults to true so terraform will wait until the KMS is applied to the master, ready and deployed
})
| `null` | no | | [manage\_all\_addons](#input\_manage\_all\_addons) | Instructs Terraform to manage all cluster addons, even if addons were installed outside of the module. If set to 'true' this module destroys any addons that were installed by other sources. | `bool` | `false` | no | diff --git a/common-dev-assets b/common-dev-assets index 2ba5cc2c..abf631a1 160000 --- a/common-dev-assets +++ b/common-dev-assets @@ -1 +1 @@ -Subproject commit 2ba5cc2c867361e8bcf34bd95f7359cc03d82b25 +Subproject commit abf631a16a48a308e609896937e1eed16b4aae4e diff --git a/ibm_catalog.json b/ibm_catalog.json index a060b57f..5fd04282 100644 --- a/ibm_catalog.json +++ b/ibm_catalog.json @@ -86,7 +86,7 @@ "crn:v1:bluemix:public:iam::::role:Viewer" ], "service_name": "Resource group only", - "notes":"Viewer access is required in the resource group you want to provision in." + "notes": "Viewer access is required in the resource group you want to provision in." }, { "role_crns": [ @@ -1084,19 +1084,19 @@ "notes": "Required for creating Virtual Private Cloud (VPC)." }, { - "service_name": "cloud-object-storage", - "role_crns": [ - "crn:v1:bluemix:public:iam::::serviceRole:Manager", - "crn:v1:bluemix:public:iam::::role:Editor" - ], - "notes": "Required for creating the OpenShift cluster's internal registry storage bucket." + "service_name": "cloud-object-storage", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "notes": "Required for creating the OpenShift cluster's internal registry storage bucket." }, { "role_crns": [ "crn:v1:bluemix:public:iam::::role:Viewer" ], "service_name": "Resource group only", - "notes":"Viewer access is required in the resource group you want to provision in." + "notes": "Viewer access is required in the resource group you want to provision in." } ], "architecture": { @@ -1246,7 +1246,7 @@ }, { "key": "access_tags", - "hidden":true, + "hidden": true, "custom_config": { "type": "array", "grouping": "deployment", @@ -1262,7 +1262,8 @@ { "key": "disable_outbound_traffic_protection" } - ] + ], + "terraform_version": "1.10.5" } ] } diff --git a/main.tf b/main.tf index 3ca457b1..6cb5177c 100644 --- a/main.tf +++ b/main.tf @@ -121,6 +121,16 @@ resource "ibm_resource_tag" "cos_access_tag" { tag_type = "access" } +############################################################################## +# OCP current version +############################################################################## + +module "get_ocp_version" { + source = "./modules/get-ocp-version" + cluster_name = var.cluster_name + ibmcloud_api_key = var.ibmcloud_api_key +} + ############################################################################## # Create a OCP Cluster ############################################################################## @@ -153,7 +163,10 @@ resource "ibm_container_vpc_cluster" "cluster" { security_groups = local.cluster_security_groups lifecycle { - ignore_changes = [kube_version] + precondition { + condition = module.get_ocp_version.ocp_version < 0 || local.ocp_version_num <= module.get_ocp_version.ocp_version || var.allow_kube_version_upgrade + error_message = "Kube version changes are disabled unless allow_kube_upgrade is set to true." + } } # default workers are mapped to the subnets that are "private" @@ -224,7 +237,11 @@ resource "ibm_container_vpc_cluster" "autoscaling_cluster" { security_groups = local.cluster_security_groups lifecycle { - ignore_changes = [worker_count, kube_version] + ignore_changes = [worker_count] + precondition { + condition = module.get_ocp_version.ocp_version < 0 || local.ocp_version_num <= module.get_ocp_version.ocp_version || var.allow_kube_version_upgrade + error_message = "Kube version changes are disabled unless allow_kube_upgrade is set to true." + } } # default workers are mapped to the subnets that are "private" diff --git a/modules/get-ocp-version/main.tf b/modules/get-ocp-version/main.tf new file mode 100644 index 00000000..457f4c8e --- /dev/null +++ b/modules/get-ocp-version/main.tf @@ -0,0 +1,12 @@ +############################################################################## +# OCP Cluster Version +############################################################################## + +data "external" "get_ocp_cluster_version" { + program = ["bash", "${path.module}/scripts/get_ocp_cluster_version.sh"] + + query = { + cluster_name = var.cluster_name + ibmcloud_api_key = var.ibmcloud_api_key + } +} diff --git a/modules/get-ocp-version/outputs.tf b/modules/get-ocp-version/outputs.tf new file mode 100644 index 00000000..391d84e6 --- /dev/null +++ b/modules/get-ocp-version/outputs.tf @@ -0,0 +1,8 @@ +############################################################################## +# Outputs +############################################################################## + +output "ocp_version" { + description = "OpenShift version of the cluster" + value = tonumber(data.external.get_ocp_cluster_version.result["ocp_version"]) +} diff --git a/modules/get-ocp-version/scripts/get_ocp_cluster_version.sh b/modules/get-ocp-version/scripts/get_ocp_cluster_version.sh new file mode 100644 index 00000000..978c492b --- /dev/null +++ b/modules/get-ocp-version/scripts/get_ocp_cluster_version.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +## Returns the OCP major.minor version for the given cluster +## Script is designed to be used with Terraform "external" data source +## It always outputs JSON (even on error) so Terraform plan/apply does not break + +set -euo pipefail + +login(){ + # Login into the IBM Cloud CLI + echo "[login] Login to IBM Cloud CLI" >&2 + DIR="$(cd "$(dirname "$0")" && pwd)" + "$DIR"/ibm_cloud_login.sh >/dev/null 2>&1 + echo "[login] Login complete" >&2 +} + +get_ocp_version(){ + local cluster_name=$1 + echo " Retrieving OCP version for cluster $cluster_name" >&2 + + local output + output=$(ibmcloud oc cluster get -c "$cluster_name" --output json 2>/dev/null || true) + + local version + version=$(echo "$output" | jq -r '.masterKubeVersion' | cut -d. -f1,2) + + if [[ -z "$version" || "$version" == "null" ]]; then + echo "Could not retrieve version for cluster $cluster_name" >&2 + version="-1" + fi + + echo "$version" +} + +## Always return JSON even if something fails +handle_error() { + echo '{"ocp_version": "-1"}' + exit 0 +} + +## Parse variables passed from Terraform "query" +## (reads stdin JSON and converts to shell vars) +eval "$(jq -r '@sh "cluster_name=\(.cluster_name) IBMCLOUD_API_KEY=\(.ibmcloud_api_key)"')" +export IBMCLOUD_API_KEY + +## Trap all errors after parsing input +trap 'handle_error' ERR + +## Login and retrieve version +login +ocp_version=$(get_ocp_version "$cluster_name") + +## Return JSON to Terraform +jq -n -r --arg ocp_version "$ocp_version" '{"ocp_version":$ocp_version}' diff --git a/modules/get-ocp-version/scripts/ibm_cloud_login.sh b/modules/get-ocp-version/scripts/ibm_cloud_login.sh new file mode 100755 index 00000000..55239dff --- /dev/null +++ b/modules/get-ocp-version/scripts/ibm_cloud_login.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -euo pipefail + +attempts=1 +# Expects the environment variable $IBMCLOUD_API_KEY to be set +until ibmcloud login -q --no-region || [ $attempts -ge 3 ]; do + attempts=$((attempts+1)) + echo "Error logging in to IBM Cloud CLI..." >&2 + sleep 5 +done diff --git a/modules/get-ocp-version/variables.tf b/modules/get-ocp-version/variables.tf new file mode 100644 index 00000000..a6671078 --- /dev/null +++ b/modules/get-ocp-version/variables.tf @@ -0,0 +1,14 @@ +############################################################################## +# Input Variables +############################################################################## + +variable "ibmcloud_api_key" { + description = "APIkey that's associated with the account to use, set via environment variable TF_VAR_ibmcloud_api_key" + type = string + sensitive = true +} + +variable "cluster_name" { + type = string + description = "Name of the target IBM Cloud OpenShift Cluster" +} diff --git a/modules/get-ocp-version/version.tf b/modules/get-ocp-version/version.tf new file mode 100644 index 00000000..e1402bf5 --- /dev/null +++ b/modules/get-ocp-version/version.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + external = { + source = "hashicorp/external" + version = ">= 2.2.3" + } + } +} diff --git a/tests/go.mod b/tests/go.mod index c21757a4..028696ed 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -2,7 +2,7 @@ module github.com/terraform-ibm-modules/terraform-ibm-base-ocp-vpc go 1.24.0 -toolchain go1.24.5 +toolchain go1.25.0 require ( github.com/gruntwork-io/terratest v0.50.0 diff --git a/tests/pr_test.go b/tests/pr_test.go index 907aba76..3bbd0a03 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -97,6 +97,7 @@ func setupQuickstartOptions(t *testing.T, prefix string) *testschematic.TestSche TarIncludePatterns: []string{ "*.tf", quickStartTerraformDir + "/*.tf", "scripts/*.sh", "kubeconfig/README.md", + "modules/get-ocp-version/*.tf", "modules/get-ocp-version/scripts/*.sh", }, TemplateFolder: quickStartTerraformDir, Tags: []string{"test-schematic"}, @@ -135,7 +136,7 @@ func TestRunFullyConfigurableInSchematics(t *testing.T) { options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ Testing: t, Prefix: "ocp-fc", - TarIncludePatterns: []string{"*.tf", fullyConfigurableTerraformDir + "/*.*", fullyConfigurableTerraformDir + "/scripts/*.*", "scripts/*.sh", "kubeconfig/README.md", "modules/kube-audit/*.*", "modules/kube-audit/kubeconfig/README.md", "modules/kube-audit/scripts/*.sh", fullyConfigurableTerraformDir + "/kubeconfig/README.md", "modules/kube-audit/helm-charts/kube-audit/*.*", "modules/kube-audit/helm-charts/kube-audit/templates/*.*"}, + TarIncludePatterns: []string{"*.tf", fullyConfigurableTerraformDir + "/*.*", fullyConfigurableTerraformDir + "/scripts/*.*", "scripts/*.sh", "kubeconfig/README.md", "modules/kube-audit/*.*", "modules/kube-audit/kubeconfig/README.md", "modules/kube-audit/scripts/*.sh", fullyConfigurableTerraformDir + "/kubeconfig/README.md", "modules/kube-audit/helm-charts/kube-audit/*.*", "modules/kube-audit/helm-charts/kube-audit/templates/*.*", "modules/get-ocp-version/*.tf", "modules/get-ocp-version/scripts/*.sh"}, TemplateFolder: fullyConfigurableTerraformDir, Tags: []string{"test-schematic"}, DeleteWorkspaceOnFail: false, @@ -171,7 +172,7 @@ func TestRunUpgradeFullyConfigurable(t *testing.T) { options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ Testing: t, Prefix: "fc-upg", - TarIncludePatterns: []string{"*.tf", fullyConfigurableTerraformDir + "/*.*", fullyConfigurableTerraformDir + "/scripts/*.*", "scripts/*.sh", "kubeconfig/README.md", "modules/kube-audit/*.*", "modules/kube-audit/kubeconfig/README.md", "modules/kube-audit/scripts/*.sh", fullyConfigurableTerraformDir + "/kubeconfig/README.md", "modules/kube-audit/helm-charts/kube-audit/*.*", "modules/kube-audit/helm-charts/kube-audit/templates/*.*"}, + TarIncludePatterns: []string{"*.tf", fullyConfigurableTerraformDir + "/*.*", fullyConfigurableTerraformDir + "/scripts/*.*", "scripts/*.sh", "kubeconfig/README.md", "modules/kube-audit/*.*", "modules/get-ocp-version/*.tf", "modules/get-ocp-version/scripts/*.sh", "modules/kube-audit/kubeconfig/README.md", "modules/kube-audit/scripts/*.sh", fullyConfigurableTerraformDir + "/kubeconfig/README.md", "modules/kube-audit/helm-charts/kube-audit/*.*", "modules/kube-audit/helm-charts/kube-audit/templates/*.*"}, TemplateFolder: fullyConfigurableTerraformDir, Tags: []string{"test-schematic"}, DeleteWorkspaceOnFail: false, diff --git a/variables.tf b/variables.tf index 711a531b..81f67581 100644 --- a/variables.tf +++ b/variables.tf @@ -2,6 +2,13 @@ # Input Variables ############################################################################## +variable "ibmcloud_api_key" { + description = "APIkey that's associated with the account to use, set via environment variable TF_VAR_ibmcloud_api_key" + type = string + sensitive = true + default = null +} + # Resource Group Variables variable "resource_group_id" { type = string @@ -40,6 +47,12 @@ variable "vpc_subnets" { description = "Metadata that describes the VPC's subnets. Obtain this information from the VPC where this cluster is created." } +variable "allow_kube_version_upgrade" { + type = bool + description = "Set to true to allow the module to upgrade the kube version of the cluster. If you wish to make any change to the kube version, set this variable to true." + default = false +} + variable "allow_default_worker_pool_replacement" { type = bool description = "(Advanced users) Set to true to allow the module to recreate a default worker pool. If you wish to make any change to the default worker pool which requires the re-creation of the default pool follow these [steps](https://github.com/terraform-ibm-modules/terraform-ibm-base-ocp-vpc?tab=readme-ov-file#important-considerations-for-terraform-and-default-worker-pool)."