diff --git a/.secrets.baseline b/.secrets.baseline index 028cff78..25003cdc 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "go.sum|^.secrets.baseline$", "lines": null }, - "generated_at": "2025-01-24T16:49:18Z", + "generated_at": "2025-02-11T22:49:03Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -76,18 +76,7 @@ "name": "TwilioKeyDetector" } ], - "results": { - "solutions/deploy/README.md": [ - { - "hashed_secret": "2254481e1661d8f017a712b0d1ad9a14fd9460a3", - "is_secret": false, - "is_verified": false, - "line_number": 134, - "type": "Secret Keyword", - "verified_result": null - } - ] - }, + "results": {}, "version": "0.13.1+ibm.62.dss", "word_list": { "file": null, diff --git a/chart/cloud-pak-deployer/templates/install-job.yaml b/chart/cloud-pak-deployer/templates/install-job.yaml index 3c9fae4e..47b4650b 100644 --- a/chart/cloud-pak-deployer/templates/install-job.yaml +++ b/chart/cloud-pak-deployer/templates/install-job.yaml @@ -75,11 +75,11 @@ spec: - '/cloud-pak-deployer/cp-deploy.sh vault set -vs cp4d_admin_cpd_{{ .Values.cluster_name }} -vsv {{ .Values.deployer.admin_password }} && /cloud-pak-deployer/cp-deploy.sh env apply -vvvv {{ .Values.deployer.accept_license_flag }}' resources: limits: - cpu: 200m + cpu: 250m memory: 512Mi requests: - cpu: 10m - memory: 64Mi + cpu: 100m + memory: 256Mi serviceAccount: {{ .Values.deployer.prefix }}-sa volumes: - name: config-volume diff --git a/chart/cloud-pak-deployer/templates/uninstall-job.yaml b/chart/cloud-pak-deployer/templates/uninstall-job.yaml index a21f6e0e..e8c06861 100644 --- a/chart/cloud-pak-deployer/templates/uninstall-job.yaml +++ b/chart/cloud-pak-deployer/templates/uninstall-job.yaml @@ -66,11 +66,11 @@ spec: - /cloud-pak-deployer/scripts/cp4d/cp4d-delete-instance.sh cpd <<< "y" resources: limits: - cpu: 200m + cpu: 250m memory: 512Mi requests: - cpu: 10m - memory: 64Mi + cpu: 100m + memory: 256Mi restartPolicy: Never securityContext: runAsUser: 0 diff --git a/examples/basic/main.tf b/examples/basic/main.tf index 6a4f0222..4ea75501 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -1,24 +1,106 @@ ############################################################################## -# ROKS Landing zone +locals { + cluster_name = var.existing_cluster_name != null ? var.existing_cluster_name : module.ocp_base[0].cluster_name + cluster_rg_id = var.existing_cluster_rg_id != null ? var.existing_cluster_rg_id : module.resource_group[0].resource_group_id +} +############################################################################### + +############################################################################## +# Resource Group ############################################################################## -module "roks_landing_zone" { - source = "git::https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone.git//patterns/roks-quickstart?ref=v6.6.1" - ibmcloud_api_key = var.ibmcloud_api_key - prefix = var.prefix - region = var.region - resource_tags = var.resource_tags +module "resource_group" { + count = var.existing_cluster_rg_id == null ? 1 : 0 + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.1.6" + # if an existing resource group is not set (null) create a new one using prefix + resource_group_name = "${var.prefix}-resource-group" +} + +######################################################################################################################## +# VPC + Subnet + Public Gateway +# +# NOTE: This is a very simple VPC with single subnet in a single zone with a public gateway enabled, that will allow +# all traffic ingress/egress by default. +# For production use cases this would need to be enhanced by adding more subnets and zones for resiliency, and +# ACLs/Security Groups for network security. +######################################################################################################################## + +resource "ibm_is_vpc" "vpc" { + name = "${var.prefix}-vpc" + resource_group = local.cluster_rg_id + address_prefix_management = "auto" + tags = var.resource_tags +} + +resource "ibm_is_public_gateway" "gateway" { + name = "${var.prefix}-gateway-1" + vpc = ibm_is_vpc.vpc.id + resource_group = local.cluster_rg_id + zone = "${var.region}-1" +} + +resource "ibm_is_subnet" "subnet_zone_1" { + name = "${var.prefix}-subnet-1" + vpc = ibm_is_vpc.vpc.id + resource_group = local.cluster_rg_id + zone = "${var.region}-1" + total_ipv4_address_count = 256 + public_gateway = ibm_is_public_gateway.gateway.id +} + +######################################################################################################################## +# OCP VPC cluster (single zone) +######################################################################################################################## + +locals { + cluster_vpc_subnets = { + default = [ + { + id = ibm_is_subnet.subnet_zone_1.id + cidr_block = ibm_is_subnet.subnet_zone_1.ipv4_cidr_block + zone = ibm_is_subnet.subnet_zone_1.zone + } + ] + } + + worker_pools = [ + { + subnet_prefix = "default" + pool_name = "default" # ibm_container_vpc_cluster automatically names default pool "default" (See https://github.com/IBM-Cloud/terraform-provider-ibm/issues/2849) + machine_type = "bx2.16x64" + operating_system = "REDHAT_8_64" + workers_per_zone = 3 # minimum of 2 is allowed when using single zone + } + ] +} + +module "ocp_base" { + count = var.existing_cluster_name == null ? 1 : 0 + source = "terraform-ibm-modules/base-ocp-vpc/ibm" + version = "3.41.7" + resource_group_id = local.cluster_rg_id + region = var.region + tags = var.resource_tags + cluster_name = var.prefix + force_delete_storage = true + vpc_id = ibm_is_vpc.vpc.id + vpc_subnets = local.cluster_vpc_subnets + worker_pools = local.worker_pools + disable_outbound_traffic_protection = true # set as True to enable outbound traffic } ############################################################################## # Deploy cloudpak_data ############################################################################## + module "cloudpak_data" { source = "../../solutions/deploy" ibmcloud_api_key = var.ibmcloud_api_key prefix = var.prefix region = var.region - cluster_name = module.roks_landing_zone.workload_cluster_id + cluster_name = local.cluster_name + cluster_rg_id = local.cluster_rg_id cloud_pak_deployer_image = "quay.io/cloud-pak-deployer/cloud-pak-deployer" cpd_admin_password = "Passw0rd" #pragma: allowlist secret cpd_entitlement_key = "entitlementKey" diff --git a/examples/basic/variables.tf b/examples/basic/variables.tf index 6271210d..5a4c1aba 100644 --- a/examples/basic/variables.tf +++ b/examples/basic/variables.tf @@ -3,7 +3,7 @@ ############################################################################## variable "ibmcloud_api_key" { - description = "The IBM Cloud platform API key needed to deploy IAM enabled resources." + description = "The IBM Cloud API key to deploy resources." type = string sensitive = true } @@ -14,8 +14,8 @@ variable "prefix" { default = "lz-roks-cp4d" validation { - error_message = "Prefix must begin with a letter and contain only lowercase letters, numbers, and - characters. Prefixes must end with a lowercase letter or number and be 13 or fewer characters." - condition = can(regex("^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$", var.prefix)) && length(var.prefix) <= 13 + error_message = "Prefix must begin with a letter and contain only lowercase letters, numbers, and - characters. Prefixes must end with a lowercase letter or number and be 16 or fewer characters." + condition = can(regex("^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$", var.prefix)) && length(var.prefix) <= 16 } } @@ -36,3 +36,19 @@ variable "install_odf_cluster_addon" { type = bool default = false } + +variable "existing_cluster_name" { + description = "Existing cluster name" + type = string + default = null + validation { + condition = can(regex("^[a-z][a-z0-9-]{0,12}[a-z0-9]$", var.existing_cluster_name)) + error_message = "Existing cluster name must begin with a letter and contain only lowercase letters, numbers, and - characters. Existing cluster names must end with a lowercase letter or number and be 13 or fewer characters." + } +} + +variable "existing_cluster_rg_id" { + description = "Existing resource group id" + type = string + default = null +} diff --git a/examples/basic/version.tf b/examples/basic/version.tf index 4c869c16..27ddf4f7 100644 --- a/examples/basic/version.tf +++ b/examples/basic/version.tf @@ -3,7 +3,7 @@ terraform { required_providers { # renovate is set up to keep provider version at the latest for all DA solutions ibm = { - source = "IBM-Cloud/ibm" + source = "ibm-cloud/ibm" version = "1.71.3" } } diff --git a/solutions/deploy/README.md b/solutions/deploy/README.md index e27740e7..733a18eb 100644 --- a/solutions/deploy/README.md +++ b/solutions/deploy/README.md @@ -101,10 +101,11 @@ You need the following permissions to run this module: | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.2.0 | +| [terraform](#requirement\_terraform) | >=1.3.0 | | [external](#requirement\_external) | >= 2.3.4 | | [helm](#requirement\_helm) | >= 2.8.0, <3.0.0 | -| [ibm](#requirement\_ibm) | >= 1.66.0, < 2.0.0 | +| [ibm](#requirement\_ibm) | 1.71.3 | +| [shell](#requirement\_shell) | 1.7.10 | ### Modules @@ -120,32 +121,34 @@ You need the following permissions to run this module: | Name | Type | |------|------| -| [ibm_container_addons.odf_cluster_addon](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/container_addons) | resource | +| [ibm_container_addons.odf_cluster_addon](https://registry.terraform.io/providers/ibm-cloud/ibm/1.71.3/docs/resources/container_addons) | resource | | [external_external.schematics](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | 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_vpc_cluster.cluster_info](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/container_vpc_cluster) | data source | +| [ibm_container_cluster_config.cluster_config](https://registry.terraform.io/providers/ibm-cloud/ibm/1.71.3/docs/data-sources/container_cluster_config) | data source | +| [ibm_container_vpc_cluster.cluster_info](https://registry.terraform.io/providers/ibm-cloud/ibm/1.71.3/docs/data-sources/container_vpc_cluster) | data source | +| [ibm_iam_auth_token.tokendata](https://registry.terraform.io/providers/ibm-cloud/ibm/1.71.3/docs/data-sources/iam_auth_token) | data source | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [cloud\_pak\_deployer\_image](#input\_cloud\_pak\_deployer\_image) | Cloud Pak Deployer image location. If not defined, it will build the image via code engine | `string` | `null` | no | -| [cloud\_pak\_deployer\_release](#input\_cloud\_pak\_deployer\_release) | Release of Cloud Pak Deployer version to use. View releases at: https://github.com/IBM/cloud-pak-deployer/releases. | `string` | `"v3.1.2"` | no | -| [cloud\_pak\_deployer\_secret](#input\_cloud\_pak\_deployer\_secret) | Image pull secret for the cloud pak deployer image |
object({
username = string
password = string
server = string
email = string
})
| `null` | no | +| [cloud\_pak\_deployer\_image](#input\_cloud\_pak\_deployer\_image) | Cloud Pak Deployer image to use. If `null`, the image will be built using Code Engine. | `string` | `null` | no | +| [cloud\_pak\_deployer\_release](#input\_cloud\_pak\_deployer\_release) | Release of Cloud Pak Deployer version to use. View releases at: https://github.com/IBM/cloud-pak-deployer/releases. | `string` | `"v3.1.3"` | no | +| [cloud\_pak\_deployer\_secret](#input\_cloud\_pak\_deployer\_secret) | Secret for accessing the Cloud Pak Deployer image. If `null`, a default secret will be created # pragma: allowlist secret. |
object({
username = string
password = string
server = string
email = string
})
| `null` | no | | [cluster\_name](#input\_cluster\_name) | Name of Red Hat OpenShift cluster to install watsonx onto | `string` | n/a | yes | -| [code\_engine\_project\_id](#input\_code\_engine\_project\_id) | If you want to use an existing project, you can pass the code engine project id and the cloud pak deployer build will be built within the existing project vs a new one being created. | `string` | `null` | no | -| [code\_engine\_project\_name](#input\_code\_engine\_project\_name) | If the variable cloud\_pak\_deployer\_image is null, it will build the image with code engine and store it within a private icr registry. Provide a name if you want to set the name. If not defined, default will be {prefix}-cpd-{random-suffix} | `string` | `null` | no | -| [cpd\_accept\_license](#input\_cpd\_accept\_license) | When set to 'true', it is understood that the user has read the terms of the Cloud Pak license(s) and agrees to the terms outlined | `bool` | `false` | no | -| [cpd\_admin\_password](#input\_cpd\_admin\_password) | Password to be used by the admin user to access the Cloud Pak for Data UI. | `string` | n/a | yes | +| [cluster\_rg\_id](#input\_cluster\_rg\_id) | Resource group id of the cluster | `string` | n/a | yes | +| [code\_engine\_project\_id](#input\_code\_engine\_project\_id) | If you want to use an existing project, you can pass the code engine project ID and the Cloud Pak Deployer build will be built within the existing project instead of creating a new one. | `string` | `null` | no | +| [code\_engine\_project\_name](#input\_code\_engine\_project\_name) | If the variable cloud\_pak\_deployer\_image is null, it will build the image with code engine and store it within a private ICR registry. Provide a name if you want to set the name. If not defined, default will be `{prefix}-cpd-{random-suffix}`. | `string` | `null` | no | +| [cpd\_accept\_license](#input\_cpd\_accept\_license) | When set to 'true', it is understood that the user has read the terms of the Cloud Pak license(s) and agrees to the terms outlined. | `bool` | `true` | no | +| [cpd\_admin\_password](#input\_cpd\_admin\_password) | Password for the Cloud Pak for Data admin user. | `string` | n/a | yes | | [cpd\_entitlement\_key](#input\_cpd\_entitlement\_key) | Cloud Pak for Data entitlement key for access to the IBM Entitled Registry. Can be fetched from https://myibm.ibm.com/products-services/containerlibrary. | `string` | n/a | yes | | [cpd\_version](#input\_cpd\_version) | Cloud Pak for Data version to install. Only version 5.x.x is supported | `string` | `"5.0.3"` | no | -| [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | APIkey that's associated with the account to use | `string` | n/a | yes | -| [install\_odf\_cluster\_addon](#input\_install\_odf\_cluster\_addon) | Install the odf cluster addon | `bool` | `true` | no | -| [odf\_config](#input\_odf\_config) | Version of ODF to install | `map(string)` |
{
"addSingleReplicaPool": "false",
"billingType": "essentials",
"clusterEncryption": "false",
"disableNoobaaLB": "false",
"enableNFS": "false",
"encryptionInTransit": "false",
"hpcsBaseUrl": "",
"hpcsEncryption": "false",
"hpcsInstanceId": "",
"hpcsSecretName": "",
"hpcsServiceName": "",
"hpcsTokenUrl": "",
"ignoreNoobaa": "true",
"numOfOsd": "1",
"ocsUpgrade": "false",
"odfDeploy": "true",
"osdDevicePaths": "",
"osdSize": "512Gi",
"osdStorageClassName": "ibmc-vpc-block-metro-10iops-tier",
"prepareForDisasterRecovery": "false",
"resourceProfile": "balanced",
"taintNodes": "false",
"useCephRBDAsDefaultStorageClass": "false",
"workerNodes": "all",
"workerPool": ""
}
| no | -| [odf\_version](#input\_odf\_version) | Version of ODF to install | `string` | `"4.16.0"` | no | -| [prefix](#input\_prefix) | A unique identifier for resources that is prepended to resources that are provisioned. Must begin with a lowercase letter and end with a lowercase letter or number. Must be 16 or fewer characters. | `string` | n/a | yes | -| [region](#input\_region) | Region where resources wills be created. To find your VPC region, use `ibmcloud is regions` command to find available regions. | `string` | n/a | yes | -| [resource\_group](#input\_resource\_group) | Resource group to provision services within. If not defined, a resource group called {prefix}-cpd will be created | `string` | `null` | no | +| [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | The IBM Cloud API key to deploy resources. | `string` | n/a | yes | +| [install\_odf\_cluster\_addon](#input\_install\_odf\_cluster\_addon) | Install the ODF cluster addon. | `bool` | `true` | no | +| [odf\_config](#input\_odf\_config) | Configuration for the ODF addon. | `map(string)` |
{
"addSingleReplicaPool": "false",
"billingType": "essentials",
"clusterEncryption": "false",
"disableNoobaaLB": "false",
"enableNFS": "false",
"encryptionInTransit": "false",
"hpcsBaseUrl": "",
"hpcsEncryption": "false",
"hpcsInstanceId": "",
"hpcsSecretName": "",
"hpcsServiceName": "",
"hpcsTokenUrl": "",
"ignoreNoobaa": "true",
"numOfOsd": "1",
"ocsUpgrade": "false",
"odfDeploy": "true",
"osdDevicePaths": "",
"osdSize": "512Gi",
"osdStorageClassName": "ibmc-vpc-block-metro-10iops-tier",
"prepareForDisasterRecovery": "false",
"resourceProfile": "balanced",
"taintNodes": "false",
"useCephRBDAsDefaultStorageClass": "false",
"workerNodes": "all",
"workerPool": ""
}
| no | +| [odf\_version](#input\_odf\_version) | Version of ODF to install. | `string` | `"4.16.0"` | no | +| [prefix](#input\_prefix) | A unique identifier for resources that is prepended to resources that are provisioned. Must begin with a lowercase letter and end with a lowercase letter or number. Must be 16 or fewer characters. | `string` | `null` | no | +| [region](#input\_region) | Region where resources will be created. To find your VPC region, use `ibmcloud is regions` command to find available regions. | `string` | n/a | yes | +| [resource\_group](#input\_resource\_group) | Resource group to provision services within. If not defined, a resource group called `{prefix}-cpd` will be created. | `string` | `null` | no | | [resource\_group\_exists](#input\_resource\_group\_exists) | Resource group exists or not within the account. | `bool` | `false` | no | | [watson\_assistant\_install](#input\_watson\_assistant\_install) | If watsonx.ai is being installed, also install watson assistant | `bool` | `false` | no | | [watson\_discovery\_install](#input\_watson\_discovery\_install) | If watsonx.ai is being installed, also install watson discovery | `bool` | `false` | no | @@ -155,7 +158,12 @@ You need the following permissions to run this module: ### Outputs -No outputs. +| Name | Description | +|------|-------------| +| [cloud\_pak\_deployer\_image](#output\_cloud\_pak\_deployer\_image) | The Cloud Pak Deployer image used. | +| [cloud\_pak\_deployer\_secret](#output\_cloud\_pak\_deployer\_secret) | The secret used for accessing the Cloud Pak Deployer image. | +| [cluster\_name](#output\_cluster\_name) | The name of the OpenShift cluster. | +| [code\_engine\_project\_name](#output\_code\_engine\_project\_name) | The name of the code engine project that was created | diff --git a/solutions/deploy/cloud-pak-deployer/variables.tf b/solutions/deploy/cloud-pak-deployer/variables.tf index 1f02bd4a..ba3bb633 100644 --- a/solutions/deploy/cloud-pak-deployer/variables.tf +++ b/solutions/deploy/cloud-pak-deployer/variables.tf @@ -17,7 +17,6 @@ variable "cloud_pak_deployer_secret" { server = string email = string }) - default = null } diff --git a/solutions/deploy/cpd-image-build/variables.tf b/solutions/deploy/cpd-image-build/variables.tf index 1b3f1333..0023ac07 100644 --- a/solutions/deploy/cpd-image-build/variables.tf +++ b/solutions/deploy/cpd-image-build/variables.tf @@ -1,5 +1,5 @@ variable "ibmcloud_api_key" { - description = "APIkey that's associated with the account to use" + description = "The IBM Cloud API key to deploy resources." type = string sensitive = true } diff --git a/solutions/deploy/cpd-image-build/version.tf b/solutions/deploy/cpd-image-build/version.tf index 9166ef9e..7526aff1 100644 --- a/solutions/deploy/cpd-image-build/version.tf +++ b/solutions/deploy/cpd-image-build/version.tf @@ -1,9 +1,9 @@ terraform { - required_version = ">= 1.2.0" + required_version = ">=1.3.0" required_providers { ibm = { - source = "IBM-Cloud/ibm" - version = ">= 1.68.1, < 2.0.0" + source = "ibm-cloud/ibm" + version = "1.71.3" } random = { source = "hashicorp/random" diff --git a/solutions/deploy/main.tf b/solutions/deploy/main.tf index ca6ed4b9..d08ed441 100644 --- a/solutions/deploy/main.tf +++ b/solutions/deploy/main.tf @@ -12,7 +12,8 @@ locals { # Retrieve the openshift cluster info data "ibm_container_vpc_cluster" "cluster_info" { - name = var.cluster_name + name = var.cluster_name + resource_group_id = var.cluster_rg_id } module "build_cpd_image" { @@ -55,8 +56,12 @@ module "watsonx_data" { } module "cloud_pak_deployer" { - depends_on = [module.watsonx_ai, module.watsonx_data, module.build_cpd_image] - source = "./cloud-pak-deployer" + depends_on = [ + module.watsonx_ai, + module.watsonx_data, + module.build_cpd_image + ] + source = "./cloud-pak-deployer" cloud_pak_deployer_config = merge( module.config.cloud_pak_deployer_config_base, { diff --git a/solutions/deploy/outputs.tf b/solutions/deploy/outputs.tf index e69de29b..0afd9799 100644 --- a/solutions/deploy/outputs.tf +++ b/solutions/deploy/outputs.tf @@ -0,0 +1,20 @@ +output "cloud_pak_deployer_image" { + description = "The Cloud Pak Deployer image used." + value = var.cloud_pak_deployer_image != null ? var.cloud_pak_deployer_image : module.build_cpd_image[0].container_registry_output_image +} + +output "cloud_pak_deployer_secret" { + description = "The secret used for accessing the Cloud Pak Deployer image." + value = var.cloud_pak_deployer_secret != null ? var.cloud_pak_deployer_secret : (var.cloud_pak_deployer_image == null ? { username : "iamapikey", password : var.ibmcloud_api_key, server : module.build_cpd_image[0].container_registry_server, email : "none" } : null) + sensitive = true +} + +output "cluster_name" { + description = "The name of the OpenShift cluster." + value = var.cluster_name +} + +output "code_engine_project_name" { + description = "The name of the code engine project that was created" + value = var.cloud_pak_deployer_image == null ? module.build_cpd_image.code_engine_project_name : null +} diff --git a/solutions/deploy/providers.tf b/solutions/deploy/providers.tf index 744e2f84..4eab448e 100644 --- a/solutions/deploy/providers.tf +++ b/solutions/deploy/providers.tf @@ -9,6 +9,14 @@ data "ibm_container_cluster_config" "cluster_config" { config_dir = local.kube_config_dir } +data "ibm_iam_auth_token" "tokendata" {} + +provider "shell" { + sensitive_environment = { + TOKEN = data.ibm_iam_auth_token.tokendata.iam_access_token + } +} + provider "helm" { kubernetes { host = data.ibm_container_cluster_config.cluster_config.host diff --git a/solutions/deploy/variables.tf b/solutions/deploy/variables.tf index fcbc84b9..cef6c2b5 100644 --- a/solutions/deploy/variables.tf +++ b/solutions/deploy/variables.tf @@ -1,5 +1,5 @@ variable "ibmcloud_api_key" { - description = "APIkey that's associated with the account to use" + description = "The IBM Cloud API key to deploy resources." type = string sensitive = true } @@ -7,20 +7,21 @@ variable "ibmcloud_api_key" { variable "prefix" { description = "A unique identifier for resources that is prepended to resources that are provisioned. Must begin with a lowercase letter and end with a lowercase letter or number. Must be 16 or fewer characters." type = string + default = null validation { - error_message = "Prefix must begin and end with a letter and contain only letters, numbers, and - characters." - condition = can(regex("^([A-z]|[a-z][-a-z0-9]*[a-z0-9])$", var.prefix)) + error_message = "Prefix must begin with a letter and contain only lowercase letters, numbers, and - characters. Prefixes must end with a lowercase letter or number and be 16 or fewer characters." + condition = can(regex("^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$", var.prefix)) && length(var.prefix) <= 16 } } variable "region" { - description = "Region where resources wills be created. To find your VPC region, use `ibmcloud is regions` command to find available regions." + description = "Region where resources will be created. To find your VPC region, use `ibmcloud is regions` command to find available regions." type = string } variable "resource_group" { - description = "Resource group to provision services within. If not defined, a resource group called {prefix}-cpd will be created" + description = "Resource group to provision services within. If not defined, a resource group called `{prefix}-cpd` will be created." type = string default = null } @@ -32,19 +33,19 @@ variable "resource_group_exists" { } variable "code_engine_project_name" { - description = "If the variable cloud_pak_deployer_image is null, it will build the image with code engine and store it within a private icr registry. Provide a name if you want to set the name. If not defined, default will be {prefix}-cpd-{random-suffix}" + description = "If the variable cloud_pak_deployer_image is null, it will build the image with code engine and store it within a private ICR registry. Provide a name if you want to set the name. If not defined, default will be `{prefix}-cpd-{random-suffix}`." type = string default = null } variable "code_engine_project_id" { - description = "If you want to use an existing project, you can pass the code engine project id and the cloud pak deployer build will be built within the existing project vs a new one being created." + description = "If you want to use an existing project, you can pass the code engine project ID and the Cloud Pak Deployer build will be built within the existing project instead of creating a new one." type = string default = null } variable "cloud_pak_deployer_image" { - description = "Cloud Pak Deployer image location. If not defined, it will build the image via code engine" + description = "Cloud Pak Deployer image to use. If `null`, the image will be built using Code Engine." type = string default = null } @@ -52,18 +53,17 @@ variable "cloud_pak_deployer_image" { variable "cloud_pak_deployer_release" { description = "Release of Cloud Pak Deployer version to use. View releases at: https://github.com/IBM/cloud-pak-deployer/releases." type = string - default = "v3.1.2" + default = "v3.1.3" } variable "cloud_pak_deployer_secret" { - description = "Image pull secret for the cloud pak deployer image" + description = "Secret for accessing the Cloud Pak Deployer image. If `null`, a default secret will be created # pragma: allowlist secret." type = object({ username = string password = string server = string email = string }) - default = null } @@ -72,20 +72,25 @@ variable "cluster_name" { type = string } +variable "cluster_rg_id" { + description = "Resource group id of the cluster" + type = string +} + variable "install_odf_cluster_addon" { - description = "Install the odf cluster addon" + description = "Install the ODF cluster addon." type = bool default = true } variable "odf_version" { - description = "Version of ODF to install" + description = "Version of ODF to install." type = string default = "4.16.0" } variable "odf_config" { - description = "Version of ODF to install" + description = "Configuration for the ODF addon." type = map(string) default = { "odfDeploy" = "true" @@ -116,15 +121,14 @@ variable "odf_config" { } } - variable "cpd_accept_license" { - default = false - description = "When set to 'true', it is understood that the user has read the terms of the Cloud Pak license(s) and agrees to the terms outlined" + description = "When set to 'true', it is understood that the user has read the terms of the Cloud Pak license(s) and agrees to the terms outlined." type = bool + default = true } variable "cpd_admin_password" { - description = "Password to be used by the admin user to access the Cloud Pak for Data UI." + description = "Password for the Cloud Pak for Data admin user." sensitive = true type = string } diff --git a/solutions/deploy/version.tf b/solutions/deploy/version.tf index ccbf4862..40acbcf7 100644 --- a/solutions/deploy/version.tf +++ b/solutions/deploy/version.tf @@ -1,9 +1,9 @@ terraform { - required_version = ">= 1.2.0" + required_version = ">=1.3.0" required_providers { ibm = { - source = "IBM-Cloud/ibm" - version = ">= 1.66.0, < 2.0.0" + source = "ibm-cloud/ibm" + version = "1.71.3" } external = { source = "hashicorp/external" @@ -13,5 +13,9 @@ terraform { source = "hashicorp/helm" version = ">= 2.8.0, <3.0.0" } + shell = { + source = "scottwinkler/shell" + version = "1.7.10" + } } } diff --git a/tests/pr_test.go b/tests/pr_test.go index 409de692..db86379e 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -2,56 +2,210 @@ package test import ( + "crypto/rand" + "encoding/base64" + "fmt" + "log" + "os" + "strings" "testing" + "github.com/gruntwork-io/terratest/modules/files" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/gruntwork-io/terratest/modules/terraform" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/common" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" ) // Ensure every example directory has a corresponding test -const defaultExampleTerraformDir = "examples/basic" - -func setupOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { - options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ - Testing: t, - TerraformDir: dir, - Prefix: prefix, - IgnoreAdds: testhelper.Exemptions{ // Ignore for consistency check - List: []string{}, +const instanceFlavorDir = "solutions/deploy" + +var permanentResources map[string]interface{} + +// Define a struct with fields that match the structure of the YAML data +const yamlLocation = "../common-dev-assets/common-go-assets/common-permanent-resources.yaml" + +func TestMain(m *testing.M) { + // Read the YAML file contents + var err error + permanentResources, err = common.LoadMapFromYaml(yamlLocation) + if err != nil { + log.Fatal(err) + } + + os.Exit(m.Run()) +} + +// A test to pass existing resources to the CloudPak DA +func TestRunStandardSolution(t *testing.T) { + t.Parallel() + // ------------------------------------------------------------------------------------ + // Provision ROK's first + // ------------------------------------------------------------------------------------ + + prefix := fmt.Sprintf("cp-ex-%s", strings.ToLower(random.UniqueId())) + realTerraformDir := "./resources" + tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) + tags := common.GetTagsFromTravis() + region := "us-south" + + // Verify ibmcloud_api_key variable is set + checkVariable := "TF_VAR_ibmcloud_api_key" + val, present := os.LookupEnv(checkVariable) + require.True(t, present, checkVariable+" environment variable not set") + require.NotEqual(t, "", val, checkVariable+" environment variable is empty") + + logger.Log(t, "Tempdir: ", tempTerraformDir) + existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: tempTerraformDir, + Vars: map[string]interface{}{ + "prefix": prefix, + "region": region, + "resource_tags": tags, }, - IgnoreUpdates: testhelper.Exemptions{ // Ignore for consistency check + // Set Upgrade to true to ensure latest version of providers and modules are used by terratest. + // This is the same as setting the -upgrade=true flag with terraform. + Upgrade: true, + }) + + terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of temp existing resource failed") + } else { + // ------------------------------------------------------------------------------------ + // Deploy Cloudpak DA passing using existing ROKS instance + // ------------------------------------------------------------------------------------ + options := testhelper.TestOptionsDefault(&testhelper.TestOptions{ + Testing: t, + TerraformDir: instanceFlavorDir, + // Do not hard fail the test if the implicit destroy steps fail to allow a full destroy of resource to occur + ImplicitRequired: false, + TerraformVars: map[string]interface{}{ + "prefix": prefix, + "region": region, + "cluster_name": terraform.Output(t, existingTerraformOptions, "workload_cluster_id"), + "cluster_rg_id": terraform.Output(t, existingTerraformOptions, "workload_rg_id"), + "cloud_pak_deployer_image": "quay.io/cloud-pak-deployer/cloud-pak-deployer", + "cpd_admin_password": GetRandomAdminPassword(t), + "cpd_entitlement_key": "entitlementKey", + "install_odf_cluster_addon": false, + }, + }) + + options.IgnoreUpdates = testhelper.Exemptions{ List: []string{ - "module.cloudpak_data.module.cloud_pak_deployer.helm_release.cloud_pak_deployer_helm_release", + "module.cloud_pak_deployer.helm_release.cloud_pak_deployer_helm_release", }, - }, - IgnoreDestroys: testhelper.Exemptions{ // Ignore for consistency check - List: []string{}, - }, - }) - return options -} + } -// Consistency test for the basic example -func TestRunBasicExample(t *testing.T) { + output, err := options.RunTestConsistency() + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") + } + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (existing resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (existing resources)") + } +} + +func TestRunStandardUpgradeSolution(t *testing.T) { t.Parallel() - options := setupOptions(t, "cp4d", defaultExampleTerraformDir) + prefix := fmt.Sprintf("cp-up-%s", strings.ToLower(random.UniqueId())) + realTerraformDir := "./resources" + tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) + tags := common.GetTagsFromTravis() + region := "us-south" - output, err := options.RunTestConsistency() - assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") + // Verify ibmcloud_api_key variable is set + checkVariable := "TF_VAR_ibmcloud_api_key" + val, present := os.LookupEnv(checkVariable) + require.True(t, present, checkVariable+" environment variable not set") + require.NotEqual(t, "", val, checkVariable+" environment variable is empty") -} + logger.Log(t, "Tempdir: ", tempTerraformDir) + existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: tempTerraformDir, + Vars: map[string]interface{}{ + "prefix": prefix, + "region": region, + "resource_tags": tags, + }, + // Set Upgrade to true to ensure latest version of providers and modules are used by terratest. + // This is the same as setting the -upgrade=true flag with terraform. + Upgrade: true, + }) -// Upgrade test (using advanced example) -func TestRunUpgradeExample(t *testing.T) { - t.Parallel() - options := setupOptions(t, "cp4dup", defaultExampleTerraformDir) + terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of temp existing resource failed") + } else { + // ------------------------------------------------------------------------------------ + // Deploy Cloudpak DA passing using existing ROKS instance + // ------------------------------------------------------------------------------------ + options := testhelper.TestOptionsDefault(&testhelper.TestOptions{ + Testing: t, + TerraformDir: instanceFlavorDir, + // Do not hard fail the test if the implicit destroy steps fail to allow a full destroy of resource to occur + ImplicitRequired: false, + TerraformVars: map[string]interface{}{ + "prefix": prefix, + "region": region, + "cluster_name": terraform.Output(t, existingTerraformOptions, "workload_cluster_id"), + "cluster_rg_id": terraform.Output(t, existingTerraformOptions, "workload_rg_id"), + "cloud_pak_deployer_image": "quay.io/cloud-pak-deployer/cloud-pak-deployer", + "cpd_admin_password": GetRandomAdminPassword(t), + "cpd_entitlement_key": "entitlementKey", + "install_odf_cluster_addon": false, + }, + }) - output, err := options.RunTestUpgrade() - if !options.UpgradeTestSkipped { - assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") + options.IgnoreUpdates = testhelper.Exemptions{ + List: []string{ + "module.cloud_pak_deployer.helm_release.cloud_pak_deployer_helm_release", + }, + } + + output, err := options.RunTestUpgrade() + if !options.UpgradeTestSkipped { + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") + } + } + + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (existing resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (existing resources)") } } + +func GetRandomAdminPassword(t *testing.T) string { + // Generate a 15 char long random string for the admin_pass + randomBytes := make([]byte, 13) + _, randErr := rand.Read(randomBytes) + require.Nil(t, randErr) // do not proceed if we can't gen a random password + + randomPass := "A1" + base64.URLEncoding.EncodeToString(randomBytes)[:13] + + return randomPass +} diff --git a/tests/resources/main.tf b/tests/resources/main.tf new file mode 100644 index 00000000..ffc0d58c --- /dev/null +++ b/tests/resources/main.tf @@ -0,0 +1,11 @@ +############################################################################## +# SLZ VPC +############################################################################## + +module "landing_zone" { + source = "git::https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone//patterns//roks-quickstart?ref=v7.0.1" + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region + prefix = var.prefix + resource_tags = var.resource_tags +} diff --git a/tests/resources/outputs.tf b/tests/resources/outputs.tf new file mode 100644 index 00000000..4411b4f8 --- /dev/null +++ b/tests/resources/outputs.tf @@ -0,0 +1,23 @@ +############################################################################## +# Outputs +############################################################################## + +output "prefix" { + value = module.landing_zone.prefix + description = "prefix" +} + +output "region" { + value = var.region + description = "Region where SLZ ROKS Cluster is deployed." +} + +output "workload_cluster_id" { + value = module.landing_zone.workload_cluster_id + description = "workload cluster ID." +} + +output "workload_rg_id" { + value = module.landing_zone.workload_rg_id + description = "workload resource group ID." +} diff --git a/tests/resources/provider.tf b/tests/resources/provider.tf new file mode 100644 index 00000000..df45ef50 --- /dev/null +++ b/tests/resources/provider.tf @@ -0,0 +1,4 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} diff --git a/tests/resources/variables.tf b/tests/resources/variables.tf new file mode 100644 index 00000000..71532bb7 --- /dev/null +++ b/tests/resources/variables.tf @@ -0,0 +1,23 @@ +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API key to deploy resources." + 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 = "slz-vpc" +} + +variable "resource_tags" { + type = list(string) + description = "Optional list of tags to be added to created resources" + default = [] +} diff --git a/tests/resources/version.tf b/tests/resources/version.tf new file mode 100644 index 00000000..050d0fcc --- /dev/null +++ b/tests/resources/version.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.0.0" + required_providers { + ibm = { + source = "ibm-cloud/ibm" + version = ">= 1.49.0, < 2.0.0" + } + } +}