diff --git a/ibm/mas_devops/roles/db2/templates/suite_jdbccfg.yml.j2 b/ibm/mas_devops/roles/db2/templates/suite_jdbccfg.yml.j2 index 7faa0b47d0..508bb94150 100644 --- a/ibm/mas_devops/roles/db2/templates/suite_jdbccfg.yml.j2 +++ b/ibm/mas_devops/roles/db2/templates/suite_jdbccfg.yml.j2 @@ -4,7 +4,11 @@ kind: Secret type: Opaque metadata: name: "jdbc-{{ db2_instance_name | lower }}-credentials" +{% if db2_instance_name.startswith('aiservice') %} + namespace: "aiservice-{{ mas_instance_id }}" +{% else %} namespace: "mas-{{ mas_instance_id }}-core" +{% endif %} {% if custom_labels is defined and custom_labels.items() %} labels: {% for key, value in custom_labels.items() %} @@ -19,7 +23,11 @@ apiVersion: config.mas.ibm.com/v1 kind: JdbcCfg metadata: name: "{{ suite_jdbccfg_name }}" +{% if db2_instance_name.startswith('aiservice') %} + namespace: "aiservice-{{ mas_instance_id }}" +{% else %} namespace: "mas-{{ mas_instance_id }}-core" +{% endif %} labels: {{ suite_jdbccfg_labels }} spec: displayName: "{{ suite_jdbccfg_name }}" diff --git a/ibm/mas_devops/roles/suite_app_config/defaults/main.yml b/ibm/mas_devops/roles/suite_app_config/defaults/main.yml index 6c68fc3d3b..71e1b20619 100644 --- a/ibm/mas_devops/roles/suite_app_config/defaults/main.yml +++ b/ibm/mas_devops/roles/suite_app_config/defaults/main.yml @@ -10,6 +10,10 @@ mas_instance_id: "{{ lookup('env', 'MAS_INSTANCE_ID') }}" mas_workspace_id: "{{ lookup('env', 'MAS_WORKSPACE_ID') }}" mas_config_dir: "{{ lookup('env', 'MAS_CONFIG_DIR') }}" +# AI Service configuration +# ----------------------------------------------------------------------------- +aiservice_instance_id: "{{ lookup('env', 'AISERVICE_INSTANCE_ID') }}" +aiservice_tenant_id: "{{ lookup('env', 'AISERVICE_TENANT_ID') }}" # 3. MAS application configuration # ----------------------------------------------------------------------------- diff --git a/ibm/mas_devops/roles/suite_app_config/tasks/manage/post-config/main.yml b/ibm/mas_devops/roles/suite_app_config/tasks/manage/post-config/main.yml index e3b9f69c82..e9ed4a8ccc 100644 --- a/ibm/mas_devops/roles/suite_app_config/tasks/manage/post-config/main.yml +++ b/ibm/mas_devops/roles/suite_app_config/tasks/manage/post-config/main.yml @@ -42,3 +42,13 @@ when: - mas_manage_attachment_configuration_mode in ["db", "cr"] - mas_manage_attachments_provider in ["filestorage", "ibm", "aws"] + +# Manage post-configuration: AI Service certificate import +- name: "Add AI Service certificates in ManageWorkspace CR" + import_role: + name: ibm.mas_devops.suite_manage_import_certs_config + when: + - aiservice_tls_cert is defined + - aiservice_tls_cert != "" + - aiservice_instance_id is defined + - aiservice_instance_id != "" diff --git a/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/main.yml b/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/main.yml index a385610443..8692e0a711 100644 --- a/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/main.yml +++ b/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/main.yml @@ -33,11 +33,19 @@ - mas_app_settings_customization_archive_url | length > 0 include_tasks: "tasks/manage/pre-config/setup-custom-archive.yml" -# Manage pre-configuration: Database encryption setup -- name: "Run Manage specific pre-configuration: Set database encryption keys" +# Manage pre-configuration: AI Service configuration setup +- name: "Run Manage specific pre-configuration: Set up AI Service integration" + when: + - aiservice_instance_id is defined + - aiservice_instance_id != "" + include_tasks: "tasks/manage/pre-config/setup-aiservice-config.yml" + +# Manage pre-configuration: Database encryption and AI Service secrets setup +- name: "Run Manage specific pre-configuration: Set database encryption keys and AI Service secrets" when: > - mas_app_settings_crypto_key is defined and mas_app_settings_crypto_key != '' and - mas_app_settings_cryptox_key is defined and mas_app_settings_cryptox_key != '' or - mas_app_settings_old_crypto_key is defined and mas_app_settings_old_crypto_key != '' and - mas_app_settings_old_cryptox_key is defined and mas_app_settings_old_cryptox_key != '' + (mas_app_settings_crypto_key is defined and mas_app_settings_crypto_key != '' and + mas_app_settings_cryptox_key is defined and mas_app_settings_cryptox_key != '') or + (mas_app_settings_old_crypto_key is defined and mas_app_settings_old_crypto_key != '' and + mas_app_settings_old_cryptox_key is defined and mas_app_settings_old_cryptox_key != '') or + (aiservice_api_key is defined and aiservice_api_key != '') include_tasks: "tasks/manage/pre-config/setup-encryption-secret.yml" diff --git a/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/setup-aiservice-config.yml b/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/setup-aiservice-config.yml new file mode 100644 index 0000000000..649ae9e3c0 --- /dev/null +++ b/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/setup-aiservice-config.yml @@ -0,0 +1,166 @@ +--- +# Manage specific steps to configure AI Service integration +# This retrieves AI Service connection details and sets them as facts +# to be included in the Manage encryption secret +# ------------------------------------------------------------------------ + +# Set AI Service namespace +- set_fact: + aiservice_namespace: "aiservice-{{ aiservice_instance_id }}" + +# Step 1: Set default tenant ID if not provided +# ------------------------------------------------------------------------ +- name: "Set default AI Service tenant ID if not provided" + set_fact: + aiservice_tenant_id: "user" + when: aiservice_tenant_id is not defined or aiservice_tenant_id == "" + +# Step 2: Construct fully qualified tenant name +# ------------------------------------------------------------------------ +- name: "Construct fully qualified AI Service tenant name" + set_fact: + aiservice_tenant_fqn: "aiservice-{{ aiservice_instance_id }}-{{ aiservice_tenant_id }}" + +- name: "Debug: AI Service tenant information" + debug: + msg: + - "AI Service tenant ID ............................... {{ aiservice_tenant_id }}" + - "AI Service tenant FQN .............................. {{ aiservice_tenant_fqn }}" + +# Step 2: Retrieve AI Service API key (tenant-specific) +# ------------------------------------------------------------------------ +- name: "Step 2: Lookup AI Service tenant API key secret" + kubernetes.core.k8s_info: + api_version: v1 + kind: Secret + name: "{{ aiservice_tenant_fqn }}----apikey-secret" + namespace: "{{ aiservice_namespace }}" + register: aiservice_apikey_secret + +- name: "Fail if AI Service tenant API key secret not found" + fail: + msg: "AI Service tenant API key secret '{{ aiservice_tenant_fqn }}----apikey-secret' not found in namespace '{{ aiservice_namespace }}'" + when: + - aiservice_apikey_secret.resources is not defined or aiservice_apikey_secret.resources | length == 0 + +- name: "Extract AI Service API key" + set_fact: + aiservice_api_key: "{{ aiservice_apikey_secret.resources[0].data.AIBROKER_APIKEY | b64decode }}" + +- name: "Verify AI Service API key was retrieved" + fail: + msg: "Could not retrieve AI Service API key from secret" + when: aiservice_api_key is not defined or aiservice_api_key == "" + +# Step 3: Retrieve AI Service URL from aibroker route +# ------------------------------------------------------------------------ +- name: "Step 3: Retrieve AI Service URL from aibroker route" + kubernetes.core.k8s_info: + api_version: route.openshift.io/v1 + kind: Route + name: "aibroker" + namespace: "{{ aiservice_namespace }}" + register: aiservice_route + +- name: "Fail if aibroker route not found" + fail: + msg: "AI Service route 'aibroker' not found in namespace '{{ aiservice_namespace }}'" + when: + - aiservice_route.resources is not defined or aiservice_route.resources | length == 0 + +- name: "Extract AI Service URL" + set_fact: + aiservice_url: "https://{{ aiservice_route.resources[0].spec.host }}/ibm/aibroker/service/rest/api/v1" + +# Step 4: Retrieve AI Service TLS certificate for import +# ------------------------------------------------------------------------ +- name: "Step 4: Lookup AI Service TLS certificate secret" + kubernetes.core.k8s_info: + api_version: v1 + kind: Secret + name: "{{ aiservice_instance_id }}-public-aibroker-tls" + namespace: "{{ aiservice_namespace }}" + register: aiservice_tls_secret + +- name: "Fail if AI Service TLS certificate secret not found" + fail: + msg: "AI Service TLS certificate secret '{{ aiservice_instance_id }}-public-aibroker-tls' not found in namespace '{{ aiservice_namespace }}'" + when: + - aiservice_tls_secret.resources is not defined or aiservice_tls_secret.resources | length == 0 + +- name: "Extract AI Service TLS certificate for post-config" + set_fact: + aiservice_tls_cert: "{{ aiservice_tls_secret.resources[0].data['ca.crt'] | b64decode }}" + +- name: "Set AI Service certificate import variables for post-config" + set_fact: + manage_certificates: "{{ [aiservice_tls_cert] }}" + manage_certificates_alias_prefix: "aiservice-{{ aiservice_instance_id }}-tls" + +# Step 6: Verify AI Service is running and healthy +# ------------------------------------------------------------------------ +- name: "Step 6: Verify AI Service is running and healthy" + block: + - name: "Set AI Service health URL" + set_fact: + aiservice_health_url: "https://{{ aiservice_route.resources[0].spec.host }}/ibm/aibroker/service/rest/api/v1/health" + + - name: "Check AI Service health endpoint" + uri: + url: "{{ aiservice_health_url }}" + method: GET + validate_certs: false + return_content: true + status_code: [200, 503] + register: aiservice_health_response + retries: 5 + delay: 10 + until: + - aiservice_health_response.status == 200 + - aiservice_health_response.json is defined + failed_when: false + + - name: "Fail if health endpoint never returned 200" + fail: + msg: "AI Service health endpoint failed to return HTTP 200 after 5 attempts. Last status: {{ aiservice_health_response.status }}" + when: aiservice_health_response.status != 200 + + - name: "Parse AI Service health response" + set_fact: + aiservice_kmodel_status: "{{ aiservice_health_response.json.kmodel | default('unknown') }}" + aiservice_healthy_status: "{{ aiservice_health_response.json.healthy | default(false) }}" + + - name: "Display AI Service health status" + debug: + msg: + - "AI Service Health Check:" + - " URL: {{ aiservice_health_url }}" + - " kmodel: {{ aiservice_kmodel_status }}" + - " healthy: {{ aiservice_healthy_status }}" + + - name: "Verify AI Service is fully healthy" + assert: + that: + - aiservice_kmodel_status == 'running' + - aiservice_healthy_status == true + success_msg: "AI Service is running and healthy" + fail_msg: | + AI Service is not fully healthy: + - kmodel: {{ aiservice_kmodel_status }} (expected: running) + - healthy: {{ aiservice_healthy_status }} (expected: true) + +# Debug output +# ------------------------------------------------------------------------ +- name: "Debug AI Service configuration" + debug: + msg: + - "AI Service instance ID ............................. {{ aiservice_instance_id }}" + - "AI Service tenant ID ............................... {{ aiservice_tenant_id }}" + - "AI Service tenant FQN .............................. {{ aiservice_tenant_fqn }}" + - "AI Service namespace ............................... {{ aiservice_namespace }}" + - "AI Service URL ..................................... {{ aiservice_url }}" + - "AI Service certificate alias ....................... {{ manage_certificates_alias_prefix }}" + - "AI Service API key retrieved ....................... Yes" + - "AI Service TLS certificate retrieved ............... Yes" + - "AI Service health status ........................... {{ aiservice_kmodel_status }} / {{ aiservice_healthy_status }}" + - "MAS instance ID .................................... {{ mas_instance_id }}" diff --git a/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/setup-encryption-secret.yml b/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/setup-encryption-secret.yml index cd5ab0522a..5142ae40ec 100644 --- a/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/setup-encryption-secret.yml +++ b/ibm/mas_devops/roles/suite_app_config/tasks/manage/pre-config/setup-encryption-secret.yml @@ -88,6 +88,56 @@ - is_encryption_secret_existing - mas_app_settings_override_encryption_secrets_flag +# Update existing secret with AI Service properties if needed +# This handles the case where the secret exists but doesn't have AI Service properties yet +- block: + - name: "Lookup existing {{ mas_app_settings_encryption_secret_name }} secret for AI Service update" + kubernetes.core.k8s_info: + api_version: v1 + kind: Secret + name: "{{ mas_app_settings_encryption_secret_name }}" + namespace: "mas-{{ mas_instance_id }}-{{ mas_app_id }}" + register: existing_secret_for_update + + - name: "Check if AI Service properties are missing in existing secret" + set_fact: + needs_aiservice_update: "{{ (existing_secret_for_update.resources[0].data['mxe.int.aibrokerapikey'] is not defined or existing_secret_for_update.resources[0].data['mxe.int.aibrokerapiurl'] is not defined or existing_secret_for_update.resources[0].data['mxe.int.aibrokertenantid'] is not defined) and (aiservice_api_key is defined and aiservice_api_key != '' and aiservice_url is defined and aiservice_url != '' and aiservice_tenant_fqn is defined and aiservice_tenant_fqn != '') }}" + + - name: "Debug: AI Service update needed" + debug: + msg: + - "AI Service properties missing in secret? ................ {{ needs_aiservice_update }}" + - "Will update existing secret with AI Service properties" + when: needs_aiservice_update + + - name: "Update existing secret with AI Service properties" + no_log: true + kubernetes.core.k8s: + state: present + definition: + apiVersion: v1 + kind: Secret + metadata: + name: "{{ mas_app_settings_encryption_secret_name }}" + namespace: "mas-{{ mas_instance_id }}-{{ mas_app_id }}" + stringData: + # Preserve existing DB encryption keys + MXE_SECURITY_CRYPTO_KEY: "{{ existing_secret_for_update.resources[0].data.MXE_SECURITY_CRYPTO_KEY | b64decode }}" + MXE_SECURITY_CRYPTOX_KEY: "{{ existing_secret_for_update.resources[0].data.MXE_SECURITY_CRYPTOX_KEY | b64decode }}" + # Preserve old keys if they exist + MXE_SECURITY_OLD_CRYPTO_KEY: "{{ existing_secret_for_update.resources[0].data.MXE_SECURITY_OLD_CRYPTO_KEY | b64decode if existing_secret_for_update.resources[0].data.MXE_SECURITY_OLD_CRYPTO_KEY is defined else '' }}" + MXE_SECURITY_OLD_CRYPTOX_KEY: "{{ existing_secret_for_update.resources[0].data.MXE_SECURITY_OLD_CRYPTOX_KEY | b64decode if existing_secret_for_update.resources[0].data.MXE_SECURITY_OLD_CRYPTOX_KEY is defined else '' }}" + # Add AI Service properties + mxe.int.aibrokerapikey: "{{ aiservice_api_key }}" + mxe.int.aibrokerapiurl: "{{ aiservice_url }}" + mxe.int.aibrokertenantid: "{{ aiservice_tenant_fqn }}" + when: needs_aiservice_update + + when: + - is_encryption_secret_existing + - not mas_app_settings_override_encryption_secrets_flag + - aiservice_api_key is defined and aiservice_api_key != '' + # then, finally create the encryption secret if does not exist # or create the new replacing secret with the new credentials if mas_app_settings_override_encryption_secrets_flag is True - name: "Create secret containing {{ mas_app_id }} database encryption keys" diff --git a/ibm/mas_devops/roles/suite_app_config/templates/manage/encryption-secrets.yml.j2 b/ibm/mas_devops/roles/suite_app_config/templates/manage/encryption-secrets.yml.j2 index e35fd2d13d..b1c6ac377e 100644 --- a/ibm/mas_devops/roles/suite_app_config/templates/manage/encryption-secrets.yml.j2 +++ b/ibm/mas_devops/roles/suite_app_config/templates/manage/encryption-secrets.yml.j2 @@ -1,5 +1,6 @@ -# secret to define manage database encryption settings -# https://www.ibm.com/docs/en/mas-cd/continuous-delivery?topic=encryption-database-scenarios +# Secret to define Manage database encryption settings and AI Service integration properties +# Database encryption: https://www.ibm.com/docs/en/mas-cd/continuous-delivery?topic=encryption-database-scenarios +# AI Service properties: mxe.int.aibrokerapikey, mxe.int.aibrokerapiurl, mxe.int.aibrokertenantid --- kind: Secret apiVersion: v1 @@ -21,3 +22,12 @@ stringData: MXE_SECURITY_OLD_CRYPTO_KEY: {{ mas_app_settings_old_crypto_key }} MXE_SECURITY_OLD_CRYPTOX_KEY: {{ mas_app_settings_old_cryptox_key }} {% endif %} +{% if aiservice_api_key is defined and aiservice_api_key !='' %} + mxe.int.aibrokerapikey: {{ aiservice_api_key }} +{% endif %} +{% if aiservice_url is defined and aiservice_url !='' %} + mxe.int.aibrokerapiurl: {{ aiservice_url }} +{% endif %} +{% if aiservice_tenant_fqn is defined and aiservice_tenant_fqn !='' %} + mxe.int.aibrokertenantid: {{ aiservice_tenant_fqn }} +{% endif %}