Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
71c2d55
[minor] Add AI Service configuration role for Manage
Divyesh-Khokhar Nov 20, 2025
c291bfa
[minor] finalize changes
Divyesh-Khokhar Nov 21, 2025
e3d5ab0
Merge branch 'master' into aiauto
Divyesh-Khokhar Dec 2, 2025
644568c
[minor] remove unwanted var
Divyesh-Khokhar Dec 4, 2025
608692b
[patch] Add aiservice_instance_id in suite_app_config role's default …
Dec 4, 2025
738bd11
Merge branch 'master' into aiauto
Dec 8, 2025
bd33d01
[patch] Change Namespace for jdbcCfg when db2-aiservice getting insta…
Dec 8, 2025
d5406b3
Merge branch 'master' into aiauto
Dec 9, 2025
ed93e9f
[minor] added steps to verify AI Service health
Divyesh-Khokhar Dec 10, 2025
89a4784
[patch] fix lint errors
Divyesh-Khokhar Dec 10, 2025
0ee0d34
Merge branch 'master' into aiauto
Divyesh-Khokhar Dec 10, 2025
8b91e08
Merge branch 'master' into aiauto
Divyesh-Khokhar Dec 12, 2025
1c16e3a
[minor] add ai-service configuration in pre-config
Divyesh-Khokhar Dec 15, 2025
c5d3c3b
[patch] Use pre-config/ remove new role which is not required
Divyesh-Khokhar Dec 15, 2025
4b944b4
Merge branch 'master' into aiauto
Divyesh-Khokhar Dec 15, 2025
a2478e9
[patch] re-build
Divyesh-Khokhar Dec 15, 2025
0a63386
[patch] keep aiservice_tenant_id as mandatory
Divyesh-Khokhar Dec 15, 2025
cfdbbb3
fix aiservice cert issue
Divyesh-Khokhar Dec 15, 2025
38041ee
set default aiservice_tenant_id for test
Divyesh-Khokhar Dec 15, 2025
d4dab18
[patch] fix already exists properties and cert issue
Divyesh-Khokhar Dec 16, 2025
2c59e8f
fix crt issue
Divyesh-Khokhar Dec 16, 2025
5d3e87d
Update setup-aiservice-config.yml
Divyesh-Khokhar Dec 16, 2025
4049c8b
debug
Divyesh-Khokhar Dec 16, 2025
12a567e
[patch] fix cert issue
Divyesh-Khokhar Dec 16, 2025
0cbdcf5
Update setup-aiservice-config.yml
Divyesh-Khokhar Dec 16, 2025
263e474
Update setup-aiservice-config.yml
Divyesh-Khokhar Dec 16, 2025
b531d2e
[patch]
Divyesh-Khokhar Dec 16, 2025
4b1be84
[patch] use new file
Divyesh-Khokhar Dec 16, 2025
8151345
use diff file
Divyesh-Khokhar Dec 16, 2025
294f044
Update workspace.yml.j2
Divyesh-Khokhar Dec 17, 2025
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
8 changes: 8 additions & 0 deletions ibm/mas_devops/roles/db2/templates/suite_jdbccfg.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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() %}
Expand All @@ -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 }}"
Expand Down
4 changes: 4 additions & 0 deletions ibm/mas_devops/roles/suite_app_config/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
# -----------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
---
# 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"
set_fact:
aiservice_tls_cert: "{{ aiservice_tls_secret.resources[0].data['ca.crt'] | b64decode }}"

- name: "Debug: Verify certificate format"
debug:
msg:
- "Certificate first 100 chars: {{ aiservice_tls_cert[:100] }}"
- "Certificate contains actual newlines: {{ '\n' in aiservice_tls_cert }}"
- "Certificate line count: {{ aiservice_tls_cert.split('\n') | length }}"

# Step 5: Set certificate import configuration
# ------------------------------------------------------------------------
- name: "Step 5: Set AI Service certificate import configuration"
set_fact:
aiservice_cert_alias: "aiservice-{{ aiservice_instance_id }}-tls1"
aiservice_cert_import:
- alias: "aiservice-{{ aiservice_instance_id }}-tls1"
crt: "{{ aiservice_tls_cert }}"

- name: "Render certificate import template to YAML"
set_fact:
aiservice_cert_import_yaml: "{{ lookup('template', 'manage/aiservice-cert.yml.j2') | from_yaml }}"

- name: "Debug: Verify certificate import structure"
debug:
msg:
- "Certificate import is a list: {{ aiservice_cert_import | type_debug }}"
- "Certificate count: {{ aiservice_cert_import | length }}"
- "Certificate crt field type: {{ aiservice_cert_import[0].crt | type_debug }}"
- "Certificate crt first 100 chars: {{ aiservice_cert_import[0].crt[:100] }}"
- "Rendered YAML type: {{ aiservice_cert_import_yaml | type_debug }}"

# 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 ....................... {{ aiservice_cert_alias }}"
- "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 }}"
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% for cert in aiservice_cert_import %}
- alias: "{{ cert.alias }}"
crt: |
{{ cert.crt | indent(4) }}
{% endfor %}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 %}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ metadata:
{{ key }}: "{{ value }}"
{% endfor %}
{% endif %}
spec: {{ mas_appws_spec }}
spec: {{ mas_appws_spec | to_nice_yaml(indent=2) | indent(2) }}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mas_appws_spec:
serverBundles: "{{ is_full_manage | ternary(mas_app_settings_server_bundles[mas_app_settings_server_bundles_size]['serverBundles'], []) }}"
defaultJMS: "{{ mas_app_settings_default_jms }}"
serverTimezone: "{{ mas_app_settings_server_timezone }}"
importedCerts: "{{ aiservice_cert_import_yaml if (aiservice_cert_import_yaml is defined and aiservice_cert_import_yaml | length > 0) else omit }}"
languages:
baseLang: "{{ mas_app_settings_base_lang | default('EN' , true) }}"
secondaryLangs: "{{ mas_app_settings_secondary_langs.split(',') if (mas_app_settings_secondary_langs is defined and mas_app_settings_secondary_langs is not none and mas_app_settings_secondary_langs | length > 0) else [] }}"
Expand Down
Loading