diff --git a/instance-applications/120-ibm-dbs-rds-database/README.md b/instance-applications/120-ibm-dbs-rds-database/README.md index db8bafb2e..efdda781b 100644 --- a/instance-applications/120-ibm-dbs-rds-database/README.md +++ b/instance-applications/120-ibm-dbs-rds-database/README.md @@ -1,3 +1,226 @@ -IBM DB2U Database -=============================================================================== -Create a Db2RDS database for a MAS app. \ No newline at end of file +# IBM DB2 RDS Database Configuration + +This Helm chart configures and initializes IBM DB2 RDS databases for MAS applications. + +## Overview + +The chart performs the following operations: +1. Creates bufferpools and tablespaces for MAS applications (Manage, Facilities, IoT) +2. Applies DB2 instance registry settings +3. Applies DB2 database configuration parameters +4. Applies DB2 instance DBM configuration +5. Configures DB2 audit settings (optional) + +## Configuration Values + +### Basic Connection Configuration + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `rds_admin_db_name` | RDS admin database name | `"rdsadmin"` | +| `host` | Database host endpoint | `"xyz.db"` | +| `port` | Database port | `50000` | +| `dbname` | Target database name | `"rds1"` | +| `user` | Database username | `"dummy"` | +| `password` | Database password | `"dummy"` | + +### Additional Configuration + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `db2_namespace` | DB2 namespace identifier | `""` | +| `mas_application_id` | MAS application ID (manage, facilities, iot) | `""` | +| `jdbc_connection_url` | JDBC connection URL | `""` | +| `replica_db` | Whether this is a replica database | `false` | + +### MAS Application-Specific Configuration + +The `mas_application_id` field determines which bufferpools, tablespaces, and configurations are created: + +#### Manage +- **Bufferpools**: MAXBUFPOOL, MAXBUFPOOLINDX, MAXTEMPBP +- **Tablespaces**: MAXDATA, MAXINDEX, MAXTEMP +- **Recommended Settings**: + - `DB2_WORKLOAD=MAXIMO` + - `AUTO_MAINT=OFF` + - `AUTO_TBL_MAINT=OFF` + - `LOCKTIMEOUT=300` + - `DATABASE_MEMORY=2199408 AUTOMATIC` + +#### Facilities (TRIRIGA) +- **Bufferpools**: TRIBUFPOOL, TRIBUFPOOLINDEX, TRITEMPBP, DEDICATEDBPDATA, DEDICATEDBPINDX, DEDICATEDBPLOB +- **Tablespaces**: TRIDATA_DATA, TRIDATA_INDX, TRITEMP (temp), DEDICATED_DATA, DEDICATED_INDEX, DEDICATED_LOBS +- **Recommended Settings**: + - `DB2_COMPATIBILITY_VECTOR=ORA` + - `DB2_PARALLEL_IO=*` + - `DB2_DEFERRED_PREPARE_SEMANTICS=YES` + - `CATALOGCACHE_SZ=2048` + - `LOCKTIMEOUT=30` + +#### IoT +- **Bufferpools**: Uses default bufferpools +- **Tablespaces**: Uses default tablespaces +- **Recommended Settings**: Default DB2 settings with auto-maintenance enabled + +### DB2 Instance Registry Configuration + +Configure DB2 instance registry variables. Example: + +```yaml +db2_instance_registry: + DB2_COMPATIBILITY_VECTOR: "ORA" + DB2_PARALLEL_IO: "*" + DB2_DEFERRED_PREPARE_SEMANTICS: "YES" + DB2_WORKLOAD: "MAXIMO" + DB2_SKIPINSERTED: "ON" + DB2AUTH: "OSAUTHDB,ALLOW_LOCAL_FALLBACK,PLUGIN_AUTO_RELOAD" +``` + +### DB2 Database Configuration + +Configure database-level parameters. Example: + +```yaml +db2_database_db_config: + AUTO_MAINT: "OFF" + AUTO_TBL_MAINT: "OFF" + CATALOGCACHE_SZ: "800" + LOCKTIMEOUT: "300" + LOGSECOND: "100" + STMTHEAP: "20000 AUTOMATIC" + DATABASE_MEMORY: "2199408 AUTOMATIC" +``` + +### DB2 Instance DBM Configuration + +Configure instance-level DBM parameters. Example: + +```yaml +db2_instance_dbm_config: + AGENT_STACK_SZ: "1024" + RQRIOBLK: "65535" + DFT_MON_STMT: "ON" + MON_HEAP_SZ: "AUTOMATIC" +``` + +### DB2 Audit Configuration + +Configure audit settings. Example: + +```yaml +db2_addons_audit_config: + enableAudit: true + applyDefaultPolicy: true +``` + +## Templates + +### 00-configmap.yaml +Creates a ConfigMap with Python script that performs all DB2 RDS initialization and configuration: +- Creates bufferpools and tablespaces based on MAS application type +- Applies DB2 instance registry settings using `CALL rdsadmin.update_db_param('database_name', 'key', 'value')` +- Applies DB2 database configuration using `CALL rdsadmin.update_db_param('database_name', 'key', 'value')` +- Applies DB2 instance DBM configuration using `CALL rdsadmin.update_db_param('database_name', 'key', 'value')` +- Configures DB2 audit settings using `CALL rdsadmin.update_db_param('database_name', 'AUDIT_BUF_SZ', '1000')` + +### 01-dbs-rds-postsync-setup.yaml +Job that runs the initialization script: +- **Sync Wave**: 136 +- Creates secret with database credentials +- Executes the Python script from ConfigMap +- Passes all configuration values as environment variables (JSON format) +- Downloads RDS SSL certificate for secure connections + +## Usage + +This chart is typically deployed via the root application at: +`root-applications/ibm-mas-instance-root/templates/120-dbs-rds-databases-app.yaml` + +The root application iterates over `ibm_dbs_rds_databases` configuration and creates a separate ArgoCD Application for each database instance, passing all configuration values to this chart. + +### Example Configurations + +#### Manage Database +```yaml +ibm_dbs_rds_databases: + - db2_namespace: rds-inst8 + db2_instance_name: rds-inst8-manage + mas_application_id: manage + host: "db2-rds-endpoint.us-east-1.rds.amazonaws.com" + port: 50000 + dbname: "MAXDB80" + rds_admin_db_name: "rdsadmin" + user: "db2inst1" + password: "" + replica_db: false + db2_instance_registry: + DB2_WORKLOAD: "MAXIMO" + DB2_SKIPINSERTED: "ON" + db2_database_db_config: + AUTO_MAINT: "OFF" + LOCKTIMEOUT: "300" + db2_addons_audit_config: + enableAudit: true + applyDefaultPolicy: true +``` + +#### Facilities Database +```yaml +ibm_dbs_rds_databases: + - db2_namespace: rds-inst8 + db2_instance_name: rds-inst8-facilities + mas_application_id: facilities + host: "db2-rds-endpoint.us-east-1.rds.amazonaws.com" + port: 50000 + dbname: "TRIDB80" + rds_admin_db_name: "rdsadmin" + user: "db2inst1" + password: "" + replica_db: true + db2_instance_registry: + DB2_COMPATIBILITY_VECTOR: "ORA" + DB2_PARALLEL_IO: "*" + db2_database_db_config: + CATALOGCACHE_SZ: "2048" + LOCKTIMEOUT: "30" + db2_addons_audit_config: + enableAudit: true + applyDefaultPolicy: true +``` + +#### IoT Database +```yaml +ibm_dbs_rds_databases: + - db2_namespace: rds-inst8 + db2_instance_name: rds-inst8-iot + mas_application_id: iot + host: "db2-rds-endpoint.us-east-1.rds.amazonaws.com" + port: 50000 + dbname: "IOTDB80" + rds_admin_db_name: "rdsadmin" + user: "db2inst1" + password: "" + replica_db: false + db2_addons_audit_config: + enableAudit: true + applyDefaultPolicy: true +``` + +## Sync Waves + +The chart uses ArgoCD sync waves to ensure proper ordering: + +1. **Wave 135**: ConfigMap with comprehensive initialization and configuration script +2. **Wave 136**: Secret and job that executes all DB2 setup: + - Creates bufferpools and tablespaces + - Applies DB2 instance registry settings + - Applies DB2 database configuration + - Applies DB2 instance DBM configuration + - Configures audit settings (if enabled) + +## Notes + +- All jobs are idempotent and can be safely re-run +- Jobs use the `mas.ibm.com/job-cleanup-group` label for automatic cleanup +- SSL/TLS is enforced for all database connections +- The RDS global certificate bundle is automatically downloaded \ No newline at end of file diff --git a/instance-applications/120-ibm-dbs-rds-database/templates/00-configmap.yaml b/instance-applications/120-ibm-dbs-rds-database/templates/00-configmap.yaml index 8bf9424b3..91de39b7b 100644 --- a/instance-applications/120-ibm-dbs-rds-database/templates/00-configmap.yaml +++ b/instance-applications/120-ibm-dbs-rds-database/templates/00-configmap.yaml @@ -189,5 +189,64 @@ data: else: print("āœ… All bufferpools and tablespaces already existed or not created — please check.") + # 3. Apply DB2 Instance Registry Configuration + db2_instance_registry = os.environ.get('DB2_INSTANCE_REGISTRY', '') + if db2_instance_registry: + print("\nšŸ“ Applying DB2 Instance Registry settings...") + import json + registry = json.loads(db2_instance_registry) + for key, value in registry.items(): + try: + sql = f"CALL rdsadmin.update_db_param('{database_name}', '{key}', '{value}')" + db2.exec_immediate(admin_conn, sql) + print(f" āœ… Set {key} = {value}") + except Exception as e: + print(f" āš ļø Failed to set {key}: {e}") + + # 4. Apply DB2 Database Configuration + db2_database_config = os.environ.get('DB2_DATABASE_CONFIG', '') + if db2_database_config: + print("\nšŸ“ Applying DB2 Database Configuration settings...") + import json + config = json.loads(db2_database_config) + for key, value in config.items(): + try: + sql = f"CALL rdsadmin.update_db_param('{database_name}', '{key}', '{value}')" + db2.exec_immediate(admin_conn, sql) + print(f" āœ… Set {key} = {value}") + except Exception as e: + print(f" āš ļø Failed to set {key}: {e}") + + # 5. Apply DB2 Instance DBM Configuration + db2_instance_dbm_config = os.environ.get('DB2_INSTANCE_DBM_CONFIG', '') + if db2_instance_dbm_config: + print("\nšŸ“ Applying DB2 Instance DBM Configuration settings...") + import json + config = json.loads(db2_instance_dbm_config) + for key, value in config.items(): + try: + sql = f"CALL rdsadmin.update_db_param('{database_name}', '{key}', '{value}')" + db2.exec_immediate(admin_conn, sql) + print(f" āœ… Set {key} = {value}") + except Exception as e: + print(f" āš ļø Failed to set {key}: {e}") + + # 6. Apply DB2 Audit Configuration + enable_audit = os.environ.get('ENABLE_AUDIT', 'false').lower() == 'true' + if enable_audit: + print("\nšŸ“ Configuring DB2 Audit...") + try: + sql = f"CALL rdsadmin.update_db_param('{database_name}', 'AUDIT_BUF_SZ', '1000')" + db2.exec_immediate(admin_conn, sql) + print(" āœ… Set AUDIT_BUF_SZ = 1000") + + apply_default_policy = os.environ.get('APPLY_DEFAULT_POLICY', 'false').lower() == 'true' + if apply_default_policy: + print(" ā„¹ļø Default audit policy would be applied here") + except Exception as e: + print(f" āš ļø Failed to configure audit: {e}") + + print("\nāœ… All DB2 configurations applied successfully") + db2.close(admin_conn) db2.close(db2_conn) diff --git a/instance-applications/120-ibm-dbs-rds-database/templates/01-dbs-rds-postsync-setup.yaml b/instance-applications/120-ibm-dbs-rds-database/templates/01-dbs-rds-postsync-setup.yaml index 16c007d46..65a323368 100644 --- a/instance-applications/120-ibm-dbs-rds-database/templates/01-dbs-rds-postsync-setup.yaml +++ b/instance-applications/120-ibm-dbs-rds-database/templates/01-dbs-rds-postsync-setup.yaml @@ -126,6 +126,24 @@ spec: value: {{ .Values.dbname | quote }} - name: SSL_CERT_PATH value: "/etc/mas/rds-ssl/global-bundle.pem" + {{- if .Values.db2_instance_registry }} + - name: DB2_INSTANCE_REGISTRY + value: {{ .Values.db2_instance_registry | toJson | quote }} + {{- end }} + {{- if .Values.db2_database_db_config }} + - name: DB2_DATABASE_CONFIG + value: {{ .Values.db2_database_db_config | toJson | quote }} + {{- end }} + {{- if .Values.db2_instance_dbm_config }} + - name: DB2_INSTANCE_DBM_CONFIG + value: {{ .Values.db2_instance_dbm_config | toJson | quote }} + {{- end }} + {{- if .Values.db2_addons_audit_config }} + - name: ENABLE_AUDIT + value: {{ .Values.db2_addons_audit_config.enableAudit | quote }} + - name: APPLY_DEFAULT_POLICY + value: {{ .Values.db2_addons_audit_config.applyDefaultPolicy | quote }} + {{- end }} volumeMounts: - name: dbs-rds-secret-store mountPath: /etc/mas/dbs-rds-creds/ diff --git a/instance-applications/120-ibm-dbs-rds-database/values.yaml b/instance-applications/120-ibm-dbs-rds-database/values.yaml index 18be43a63..901c8b02c 100644 --- a/instance-applications/120-ibm-dbs-rds-database/values.yaml +++ b/instance-applications/120-ibm-dbs-rds-database/values.yaml @@ -1,3 +1,4 @@ +# Basic connection configuration rds_admin_db_name: "rdsadmin" host: "xyz.db" port: 50000 @@ -5,6 +6,27 @@ dbname: "rds1" user: "dummy" password: "dummy" +# Additional basic values +db2_namespace: "" +mas_application_id: "" # manage, facilities, or iot +jdbc_connection_url: "" +replica_db: false + +# DB2 Instance Registry Configuration +db2_instance_registry: {} + +# DB2 Database Configuration +db2_database_db_config: {} + +# DB2 Audit Configuration +db2_addons_audit_config: + enableAudit: false + applyDefaultPolicy: false + +# DB2 Instance DBM Configuration +db2_instance_dbm_config: {} + +# Script and application configuration db2_rds_instance_name: dbs-rds-inst1-manage scriptConfigMapName: dbs-rds-init-script -mas_application_id: \ No newline at end of file +mas_application_id: "" \ No newline at end of file diff --git a/instance-applications/130-ibm-jdbc-config/templates/00-presync-create-db2-rds-user-Job.yaml b/instance-applications/130-ibm-jdbc-config/templates/00-presync-create-db2-rds-user-Job.yaml new file mode 100644 index 000000000..d7a3f6519 --- /dev/null +++ b/instance-applications/130-ibm-jdbc-config/templates/00-presync-create-db2-rds-user-Job.yaml @@ -0,0 +1,358 @@ +{{- if eq .Values.jdbc_type "rds-db2" }} + + {{- /* + Use the build/bin/set-cli-image-digest.sh script to update this value across all charts. + */}} + {{- $_cli_image_digest := "sha256:55b5d6dd185503f14c112836a9a4899347d28e7b6545e0b9cf21d87f9526fb40" }} + + {{ $ns := printf "mas-%s-core" .Values.instance_id }} + {{ $prefix := printf "pre-jdbc-nonadmin-%s" .Values.mas_config_name }} + {{ $sa := printf "%s-sa" $prefix }} + {{ $np := printf "%s-np" $prefix }} + {{ $job := printf "%s-job" $prefix }} + +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: {{ $sa }} + namespace: {{ $ns }} + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/hook-delete-policy: HookSucceeded,BeforeHookCreation + {{- if .Values.custom_labels }} +labels: + {{ .Values.custom_labels | toYaml | indent 4 }} + {{- end }} + +--- +# Permit outbound communication by the Job pods +# (Needed to communicate with RDS) +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: {{ $np }} + namespace: {{ $ns }} + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/hook-delete-policy: HookSucceeded,BeforeHookCreation + {{- if .Values.custom_labels }} +labels: + {{ .Values.custom_labels | toYaml | indent 4 }} + {{- end }} +spec: + podSelector: + matchLabels: + app: {{ $job }} + egress: + - {} + policyTypes: + - Egress + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ $job }} + namespace: {{ $ns }} + annotations: + argocd.argoproj.io/hook: PreSync + argocd.argoproj.io/hook-delete-policy: HookSucceeded,BeforeHookCreation + {{- if .Values.custom_labels }} +labels: + {{ .Values.custom_labels | toYaml | indent 4 }} + {{- end }} +spec: + template: + metadata: + labels: + app: {{ $job }} + {{- if .Values.custom_labels }} + {{ .Values.custom_labels | toYaml | indent 8 }} + {{- end }} +spec: + containers: + - name: run + image: quay.io/ibmmas/cli@{{ $_cli_image_digest }} + imagePullPolicy: IfNotPresent + resources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 10m + memory: 64Mi + env: + - name: JDBC_CONNECTION_URL + value: "{{ .Values.jdbc_connection_url }}" + - name: JDBC_INSTANCE_USERNAME + value: "{{ .Values.jdbc_instance_username }}" + - name: JDBC_INSTANCE_PASSWORD + value: "{{ .Values.jdbc_instance_password }}" + - name: MAS_APP_ID + value: "{{ .Values.mas_application_id }}" + command: + - /bin/sh + - -c + - | + + set -e + + echo "" + echo "================================================================================" + echo "Settings" + echo "================================================================================" + echo "JDBC_CONNECTION_URL ................. ${JDBC_CONNECTION_URL}" + echo "JDBC_INSTANCE_USERNAME .............. ${JDBC_INSTANCE_USERNAME}" + echo "MAS_APP_ID .......................... ${MAS_APP_ID}" + + echo "" + echo "Installing required Python packages..." + echo "--------------------------------------------------------------------------------" + pip install ibm_db + + echo "" + echo "Downloading RDS SSL certificate..." + echo "--------------------------------------------------------------------------------" + mkdir -p /tmp/rds-ssl + curl -sSL https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o /tmp/rds-ssl/global-bundle.pem + echo "āœ… Certificate downloaded" + + # Path to the generated Python script + RDS_USER_SCRIPT="/tmp/create_rds_nonadmin_users.py" + + echo "" + echo "Create ${RDS_USER_SCRIPT}" + echo "--------------------------------------------------------------------------------" + + # Generate a Python script to create non-admin users and grant permissions + cat > ${RDS_USER_SCRIPT} << 'EOFPYTHON' + import ibm_db + import os + import sys + + import re + + # Get environment variables + jdbc_connection_url = os.getenv('JDBC_CONNECTION_URL') + jdbc_username = os.getenv('JDBC_INSTANCE_USERNAME') + jdbc_password = os.getenv('JDBC_INSTANCE_PASSWORD') + mas_app_id = os.getenv('MAS_APP_ID', 'manage') + ssl_cert_path = '/tmp/rds-ssl/global-bundle.pem' + + # Parse JDBC connection URL to extract host, port, and database + # Format: jdbc:db2://host:port/database + match = re.match(r'jdbc:db2://([^:]+):(\d+)/(.+)', jdbc_connection_url) + if not match: + print(f"āŒ Failed to parse JDBC connection URL: {jdbc_connection_url}") + sys.exit(1) + + rds_host = match.group(1) + rds_port = match.group(2) + rds_dbname = match.group(3).split(':')[0] # Remove any additional parameters + + # Build connection string with SSL + conn_str = ( + f"DATABASE={rds_dbname};" + f"HOSTNAME={rds_host};" + f"PORT={rds_port};" + f"PROTOCOL=TCPIP;" + f"UID={jdbc_username};" + f"PWD={jdbc_password};" + f"Security=SSL;" + f"SSLServerCertificate={ssl_cert_path};" + ) + + print(f"Connecting to RDS DB2 instance: {rds_host}:{rds_port}/{rds_dbname}") + print(f"MAS Application ID: {mas_app_id}") + print(f"JDBC Username: {jdbc_username}") + + # Option 2: Create TWO types of users + # 1. Admin users: We CREATE these with DBADM (for schema management) + # 2. Non-admin user: We FETCH from AWS Secrets Manager and grant only app roles (for runtime) + + # Admin users to CREATE (with DBADM) + admin_users_to_create = [ + { + 'username': 'DB2_manage', + 'role': 'DBADM', + 'description': 'Manage Admin User (DBADM for schema management)', + 'app_type': 'manage' + }, + { + 'username': 'DB2_facilities', + 'role': 'DBADM', + 'description': 'Facilities Admin User (DBADM for schema management)', + 'app_type': 'facilities' + }, + { + 'username': 'DB2_iot', + 'role': 'DBADM', + 'description': 'IoT Admin User (DBADM for schema management)', + 'app_type': 'iot' + } + ] + + # Non-admin user from AWS Secrets Manager (grant only app roles) + # Note: IoT does NOT get non-admin roles - only admin user with DBADM + nonadmin_user_roles = [ + { + 'username': jdbc_username, # From AWS Secrets Manager + 'grants': ['MAXIMO_READ', 'MAXIMO_WRITE', 'MAXIMO_SEQ', 'EXPLAIN'], + 'description': 'Manage Application User (from AWS Secrets Manager)', + 'app_type': 'manage' + }, + { + 'username': jdbc_username, + 'grants': ['TRIDATA_READ', 'TRIDATA_WRITE'], + 'description': 'Facilities Application User (from AWS Secrets Manager)', + 'app_type': 'facilities' + } + # IoT: No non-admin roles - uses admin user (DB2_iot) with DBADM only + ] + + print(f"\nāœ… Creating users:") + print(f" 1. Admin users: DB2_manage, DB2_facilities, DB2_iot (DBADM)") + print(f" 2. Non-admin user: {jdbc_username} (App roles for Manage & Facilities only)") + print(f" Note: IoT uses admin user (DB2_iot) with DBADM only") + + try: + # Connect to the database + conn = ibm_db.connect(conn_str, "", "") + print("āœ… Successfully connected to RDS DB2") + + # ======================================================================== + # STEP 1: Create Admin Users with DBADM + # ======================================================================== + print(f"\n{'='*80}") + print("STEP 1: Creating Admin Users (DBADM)") + print(f"{'='*80}") + + for admin_user in admin_users_to_create: + username = admin_user['username'] + role_to_assign = admin_user['role'] + description = admin_user['description'] + app_type = admin_user['app_type'] + + print(f"\n--- Processing: {description} ---") + print(f"Username: {username}") + print(f"App Type: {app_type}") + + # Check if user exists + check_user_sql = f""" + SELECT COUNT(*) as user_count + FROM SYSIBM.SYSDBAUTH + WHERE GRANTEE = '{username}' + """ + + try: + stmt = ibm_db.exec_immediate(conn, check_user_sql) + result = ibm_db.fetch_assoc(stmt) + user_exists = result['USER_COUNT'] > 0 + + if user_exists: + print(f"ā„¹ļø User {username} already exists") + else: + print(f"āœ… Creating user {username} with comprehensive admin privileges") + + # Grant comprehensive admin privileges to user + admin_grants = [ + f"GRANT DBADM, CREATETAB, BINDADD, CONNECT, CREATE_NOT_FENCED_ROUTINE, IMPLICIT_SCHEMA, LOAD, CREATE_EXTERNAL_ROUTINE, QUIESCE_CONNECT ON DATABASE TO USER {username}", + f"GRANT USE OF TABLESPACE MAXDATA TO USER {username}", + f"GRANT DBADM WITHOUT DATAACCESS WITHOUT ACCESSCTRL ON DATABASE TO USER {username}", + f"GRANT SECADM ON DATABASE TO USER {username}", + f"GRANT DATAACCESS ON DATABASE TO USER {username}", + f"GRANT ACCESSCTRL ON DATABASE TO USER {username}" + ] + + for grant_sql in admin_grants: + try: + ibm_db.exec_immediate(conn, grant_sql) + print(f"āœ… Executed: {grant_sql}") + except Exception as e: + print(f"āš ļø Error executing grant: {str(e)}") + print(f" SQL: {grant_sql}") + + except Exception as e: + print(f"āš ļø Error processing user {username}: {str(e)}") + + # ======================================================================== + # STEP 2: Grant Application Roles to Non-Admin User (from AWS Secrets) + # ======================================================================== + print(f"\n{'='*80}") + print("STEP 2: Granting Application Roles to Non-Admin User") + print(f"{'='*80}") + print(f"Non-admin user: {jdbc_username} (from AWS Secrets Manager)") + + # Check if non-admin user exists + check_user_sql = f""" + SELECT COUNT(*) as user_count + FROM SYSIBM.SYSDBAUTH + WHERE GRANTEE = '{jdbc_username}' + """ + + try: + stmt = ibm_db.exec_immediate(conn, check_user_sql) + result = ibm_db.fetch_assoc(stmt) + user_exists = result['USER_COUNT'] > 0 + + if user_exists: + print(f"āœ… User {jdbc_username} exists in database") + else: + print(f"āš ļø User {jdbc_username} not found in database") + print(f" Note: User should be created externally in AWS RDS") + + except Exception as e: + print(f"āš ļø Error checking user {jdbc_username}: {str(e)}") + + # Grant application-specific roles to non-admin user + for user_info in nonadmin_user_roles: + grants = user_info['grants'] + description = user_info['description'] + app_type = user_info['app_type'] + + print(f"\n--- Processing: {description} ---") + print(f"App Type: {app_type}") + + # Grant application role permissions + if grants: + print(f"āœ… Granting {app_type} application roles to {jdbc_username}...") + for grant_role in grants: + try: + grant_role_sql = f"GRANT ROLE {grant_role} TO USER {jdbc_username}" + ibm_db.exec_immediate(conn, grant_role_sql) + print(f"āœ… Granted role {grant_role} to {jdbc_username}") + except Exception as e: + print(f"āš ļø Error granting role {grant_role}: {str(e)}") + print(f" Note: Role {grant_role} may not exist yet. It will be created by the role creation job.") + + print(f"\n{'='*80}") + print("āœ… User creation and role assignment completed successfully") + print(f"{'='*80}") + print(f"Admin users: DB2_manage, DB2_facilities, DB2_iot (DBADM)") + print(f"Non-admin user: {jdbc_username} (Application roles only)") + + # Close connection + ibm_db.close(conn) + + except Exception as e: + print(f"āŒ Error: {str(e)}") + sys.exit(1) + EOFPYTHON + # IMPORTANT: Do not make any changes to the "EOFPYTHON" line above (including its indentation) + + echo "" + echo "Executing ${RDS_USER_SCRIPT}" + echo "--------------------------------------------------------------------------------" + python ${RDS_USER_SCRIPT} || exit $? + + echo "" + echo "āœ… RDS app-specific admin user creation job completed successfully" + + restartPolicy: Never + serviceAccountName: {{ $sa }} + volumes: [] +backoffLimit: 4 + + {{- end }} + diff --git a/instance-applications/510-550-ibm-mas-suite-app-config/templates/703-postsync-rds-app-roles.yaml b/instance-applications/510-550-ibm-mas-suite-app-config/templates/703-postsync-rds-app-roles.yaml new file mode 100644 index 000000000..75fa7d90f --- /dev/null +++ b/instance-applications/510-550-ibm-mas-suite-app-config/templates/703-postsync-rds-app-roles.yaml @@ -0,0 +1,467 @@ +{{- if not (empty .Values.mas_rds_databases) }} + +{{- /* +Use the build/bin/set-cli-image-digest.sh script to update this value across all charts. +*/}} +{{- $_cli_image_digest := "sha256:55b5d6dd185503f14c112836a9a4899347d28e7b6545e0b9cf21d87f9526fb40" }} + +{{ $app_ns := .Values.mas_app_namespace }} +{{ $np_name := "postsync-app-rds-roles-np" }} +{{ $sa_name := "postsync-app-rds-roles-sa" }} +{{ $job_label := "postsync-app-rds-roles-job" }} + +--- +{{- /* + Permit outbound communication by the Job pod (Needed to communicate with RDS) + This single policy is shared by all per-rds job instances +*/}} +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: {{ $np_name }} + namespace: {{ $app_ns }} + annotations: + argocd.argoproj.io/sync-wave: "703" +{{- if .Values.custom_labels }} + labels: +{{ .Values.custom_labels | toYaml | indent 4 }} +{{- end }} +spec: + podSelector: + matchLabels: + app: {{ $job_label }} + egress: + - {} + policyTypes: + - Egress + +--- +{{- /* + Service account for the job + This single service account is shared by all per-rds job instances +*/}} +kind: ServiceAccount +apiVersion: v1 +metadata: + name: "{{ $sa_name }}" + namespace: "{{ $app_ns }}" + annotations: + argocd.argoproj.io/sync-wave: "703" +{{- if .Values.custom_labels }} + labels: +{{ .Values.custom_labels | toYaml | indent 4 }} +{{- end }} + + +{{- /* + A separate CronJob is created in the app namespace per RDS instance reserved for use by MAS applications (Manage or Facilities). + This job runs periodically to check if application-specific tables exist and creates required roles. + + NOTE: When inside the range loop below, make sure you prefix any references to chart values NOT under .Values.manage_rds_databases with $. + For example: {{ $.Values.custom_labels }} (instead of {{ .Values.custom_labels }} ) +*/}} +{{- range $i, $db := .Values.mas_rds_databases }} + +{{- /* +Meaningful prefix for the job resource name. Must be under 52 chars in length to leave room for the 11 chars reserved for '-' and $_job_hash. +*/}} +{{- $_job_name_prefix := "postsync-rds-roles" }} + +{{- /* +A dict of values that influence the behaviour of the job in some way. +Any changes to values in this dict will trigger a rerun of the job. +Included in $_job_hash (see below). +*/}} +{{- $_job_config_values := $db }} + +{{- /* +Increment this value whenever you make a change to an immutable field of the Job resource. +E.g. passing in a new environment variable. +Included in $_job_hash (see below). +*/}} +{{- $_job_version := "v1" }} + +{{- /* +10 char hash appended to the job name taking into account $_job_config_values, $_job_version and $_cli_image_digest +This is to ensure ArgoCD will create a new job resource instead of attempting (and failing) to update an +immutable field of any existing Job resource. +*/}} +{{- $_job_hash := print ( $_job_config_values | toYaml ) $_cli_image_digest $_job_version | adler32sum }} + +{{- $_cronjob_name := join "-" (list $_job_name_prefix $_job_hash) }} + +{{- /* +Set as the value for the mas.ibm.com/job-cleanup-group label on the Job resource. + +When the auto_delete flag is not set on the root application, a CronJob in the cluster uses this label +to identify old Job resources that should be pruned on behalf of ArgoCD. + +Any Job resources in the same namespace that have the mas.ibm.com/job-cleanup-group with this value +will be considered to belong to the same cleanup group. All but the most recent (i.e. with the latest "creation_timestamp") +Jobs will be automatically deleted. + +$_job_cleanup_group can usually just be based on $_job_name_prefix. There are some special cases +where multiple Jobs are created in our templates using a Helm loop. In those cases, additional discriminators +must be added to $_job_cleanup_group. +NOTE: this is one of those cases; we need a separate cleanup group for each per-rds Job. + +By convention, we sha1sum this value to guarantee we never exceed the 63 char limit regardless of which discriminators +are required here. +*/}} +{{- $_job_cleanup_group := cat $_job_name_prefix $db.rds_instance_name | sha1sum }} + +{{ $rds_host := $db.rds_host }} +{{ $rds_port := $db.rds_port }} +{{ $rds_dbname := $db.rds_dbname }} +{{ $rds_instance_name := $db.rds_instance_name }} +{{ $rds_admin_user := $db.rds_admin_user }} +{{ $rds_admin_password := $db.rds_admin_password }} + +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ $_cronjob_name }} + namespace: "{{ $app_ns }}" + annotations: + argocd.argoproj.io/sync-wave: "704" + labels: + mas.ibm.com/job-cleanup-group: {{ $_job_cleanup_group }} +{{- if $.Values.custom_labels }} +{{ $.Values.custom_labels | toYaml | indent 4 }} +{{- end }} +spec: + schedule: "*/30 * * * *" # Run every 30 minutes + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 1 + jobTemplate: + metadata: + labels: + app: {{ $job_label }} +{{- if $.Values.custom_labels }} +{{ $.Values.custom_labels | toYaml | indent 8 }} +{{- end }} + spec: + template: + metadata: + labels: + app: {{ $job_label }} +{{- if $.Values.custom_labels }} +{{ $.Values.custom_labels | toYaml | indent 12 }} +{{- end }} + spec: + containers: + - name: run + image: {{ $.Values.cli_image_repo | default "quay.io/ibmmas/cli" }}@{{ $_cli_image_digest }} + imagePullPolicy: IfNotPresent + resources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 10m + memory: 64Mi + env: + - name: RDS_HOST + value: "{{ $rds_host }}" + - name: RDS_PORT + value: "{{ $rds_port }}" + - name: RDS_DBNAME + value: "{{ $rds_dbname }}" + - name: RDS_INSTANCE_NAME + value: "{{ $rds_instance_name }}" + - name: RDS_ADMIN_USER + value: "{{ $rds_admin_user }}" + - name: RDS_ADMIN_PASSWORD + value: "{{ $rds_admin_password }}" + - name: MAS_APP_ID + value: "{{ $.Values.mas_app_id }}" + - name: OPERATIONAL_MODE + value: "{{ $.Values.operational_mode }}" + + volumeMounts: [] + command: + - /bin/sh + - -c + - | + + set -e + source /mascli/functions/gitops_utils + + echo "" + echo "================================================================================" + echo "Settings" + echo "================================================================================" + echo "RDS_INSTANCE_NAME ................... ${RDS_INSTANCE_NAME}" + echo "RDS_DBNAME .......................... ${RDS_DBNAME}" + echo "RDS_HOST ............................ ${RDS_HOST}" + echo "RDS_PORT ............................ ${RDS_PORT}" + echo "OPERATIONAL_MODE .................... ${OPERATIONAL_MODE}" + + echo "" + echo "Installing required Python packages..." + echo "--------------------------------------------------------------------------------" + pip install ibm_db + + echo "" + echo "Downloading RDS SSL certificate..." + echo "--------------------------------------------------------------------------------" + mkdir -p /tmp/rds-ssl + curl -sSL https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o /tmp/rds-ssl/global-bundle.pem + echo "āœ… Certificate downloaded" + + # Path to the generated Python script + RDS_ROLES_SCRIPT="/tmp/create_rds_roles.py" + + echo "" + echo "Create ${RDS_ROLES_SCRIPT}" + echo "--------------------------------------------------------------------------------" + + # Generate a Python script to check table and create roles + cat > ${RDS_ROLES_SCRIPT} << 'EOFPYTHON' + import ibm_db + import os + import sys + + # Get environment variables + rds_host = os.getenv('RDS_HOST') + rds_port = os.getenv('RDS_PORT') + rds_dbname = os.getenv('RDS_DBNAME') + rds_admin_user = os.getenv('RDS_ADMIN_USER') + rds_admin_password = os.getenv('RDS_ADMIN_PASSWORD') + ssl_cert_path = '/tmp/rds-ssl/global-bundle.pem' + + # Build connection string with SSL + conn_str = ( + f"DATABASE={rds_dbname};" + f"HOSTNAME={rds_host};" + f"PORT={rds_port};" + f"PROTOCOL=TCPIP;" + f"UID={rds_admin_user};" + f"PWD={rds_admin_password};" + f"Security=SSL;" + f"SSLServerCertificate={ssl_cert_path};" + ) + + print(f"Connecting to RDS DB2 instance: {rds_host}:{rds_port}/{rds_dbname}") + print("Creating roles for MAS applications: manage, facilities") + print("Note: IoT uses admin user (DB2_iot) with full admin privileges - no separate roles") + + try: + # Connect to the database + conn = ibm_db.connect(conn_str, "", "") + print("āœ… Successfully connected to RDS DB2") + + # Define app configurations with table checks and roles + # Note: IoT is NOT included - it uses admin user (DB2_iot) with full privileges only + app_configs = [ + { + 'app_name': 'manage', + 'schema': 'MAXIMO', + 'check_table': 'MAXUSERS', + 'roles': ['MAXIMO_READ', 'MAXIMO_WRITE', 'MAXIMO_SEQ', 'EXPLAIN'] + }, + { + 'app_name': 'facilities', + 'schema': 'TRIDATA', + 'check_table': 'TRIDATA_DATA', + 'roles': ['TRIDATA_READ', 'TRIDATA_WRITE'] + } + # IoT: No roles created - uses DB2_iot admin user with full privileges + ] + + # Process each app configuration + for app_config in app_configs: + app_name = app_config['app_name'] + schema_name = app_config['schema'] + table_name = app_config['check_table'] + roles = app_config['roles'] + + print(f"\n{'='*60}") + print(f"Processing {app_name.upper()} application") + print(f"{'='*60}") + + # Check if the required table exists + check_table_sql = f""" + SELECT COUNT(*) as table_count + FROM SYSCAT.TABLES + WHERE TABSCHEMA = '{schema_name}' AND TABNAME = '{table_name}' + """ + + stmt = ibm_db.exec_immediate(conn, check_table_sql) + result = ibm_db.fetch_assoc(stmt) + table_exists = result['TABLE_COUNT'] > 0 + + if not table_exists: + print(f"āš ļø Table {schema_name}.{table_name} does not exist yet. Skipping {app_name} role creation.") + continue + + print(f"āœ… Table {schema_name}.{table_name} exists. Proceeding with role creation...") + + # Create roles if they don't exist + for role in roles: + try: + # Check if role exists + check_role_sql = f""" + SELECT COUNT(*) as role_count + FROM SYSCAT.ROLES + WHERE ROLENAME = '{role}' + """ + stmt = ibm_db.exec_immediate(conn, check_role_sql) + result = ibm_db.fetch_assoc(stmt) + role_exists = result['ROLE_COUNT'] > 0 + + if not role_exists: + create_role_sql = f"CREATE ROLE {role}" + ibm_db.exec_immediate(conn, create_role_sql) + print(f"āœ… Created role: {role}") + else: + print(f"ā„¹ļø Role {role} already exists") + + except Exception as e: + print(f"āš ļø Error creating role {role}: {str(e)}") + + # Grant permissions based on app type + if app_name == 'manage': + # Grant permissions to MAXIMO_READ role + print("\nāœ… Granting permissions to MAXIMO_READ role...") + read_grants = [ + "GRANT SELECT ON TABLE MAXIMO.MAXUSERS TO ROLE MAXIMO_READ", + "GRANT SELECT ON SCHEMA MAXIMO TO ROLE MAXIMO_READ", + ] + + for grant_sql in read_grants: + try: + ibm_db.exec_immediate(conn, grant_sql) + print(f"āœ… Executed: {grant_sql}") + except Exception as e: + print(f"āš ļø Error executing grant: {str(e)}") + + # Grant permissions to MAXIMO_WRITE role + print("\nāœ… Granting permissions to MAXIMO_WRITE role...") + write_grants = [ + "GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE MAXIMO.MAXUSERS TO ROLE MAXIMO_WRITE", + "GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA MAXIMO TO ROLE MAXIMO_WRITE", + ] + + for grant_sql in write_grants: + try: + ibm_db.exec_immediate(conn, grant_sql) + print(f"āœ… Executed: {grant_sql}") + except Exception as e: + print(f"āš ļø Error executing grant: {str(e)}") + + # Grant permissions to MAXIMO_SEQ role + print("\nāœ… Granting permissions to MAXIMO_SEQ role...") + seq_grants = [ + "GRANT USAGE ON SEQUENCE MAXIMO.MAXSEQ TO ROLE MAXIMO_SEQ", + "GRANT ALTER ON SEQUENCE MAXIMO.MAXSEQ TO ROLE MAXIMO_SEQ", + ] + + for grant_sql in seq_grants: + try: + ibm_db.exec_immediate(conn, grant_sql) + print(f"āœ… Executed: {grant_sql}") + except Exception as e: + print(f"āš ļø Error executing grant: {str(e)}") + + # Grant permissions to EXPLAIN role + print("\nāœ… Granting permissions to EXPLAIN role...") + explain_grants = [ + "GRANT EXECUTE ON PACKAGE NULLID.SYSSH200 TO ROLE EXPLAIN", + "GRANT SELECT ON TABLE SYSTOOLS.EXPLAIN_INSTANCE TO ROLE EXPLAIN", + ] + + for grant_sql in explain_grants: + try: + ibm_db.exec_immediate(conn, grant_sql) + print(f"āœ… Executed: {grant_sql}") + except Exception as e: + print(f"āš ļø Error executing grant: {str(e)}") + + elif app_name == 'facilities': + # Grant permissions to TRIDATA_READ role + print("\nāœ… Granting permissions to TRIDATA_READ role...") + read_grants = [ + "GRANT SELECT ON TABLE TRIDATA.TRIDATA_DATA TO ROLE TRIDATA_READ", + "GRANT SELECT ON SCHEMA TRIDATA TO ROLE TRIDATA_READ", + ] + + for grant_sql in read_grants: + try: + ibm_db.exec_immediate(conn, grant_sql) + print(f"āœ… Executed: {grant_sql}") + except Exception as e: + print(f"āš ļø Error executing grant: {str(e)}") + + # Grant permissions to TRIDATA_WRITE role + print("\nāœ… Granting permissions to TRIDATA_WRITE role...") + write_grants = [ + "GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE TRIDATA.TRIDATA_DATA TO ROLE TRIDATA_WRITE", + "GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA TRIDATA TO ROLE TRIDATA_WRITE", + ] + + for grant_sql in write_grants: + try: + ibm_db.exec_immediate(conn, grant_sql) + print(f"āœ… Executed: {grant_sql}") + except Exception as e: + print(f"āš ļø Error executing grant: {str(e)}") + + elif app_name == 'iot': + # Grant permissions to IOT_READ role + print("\nāœ… Granting permissions to IOT_READ role...") + read_grants = [ + "GRANT SELECT ON TABLE IOT.IOT_DATA TO ROLE IOT_READ", + "GRANT SELECT ON SCHEMA IOT TO ROLE IOT_READ", + ] + + for grant_sql in read_grants: + try: + ibm_db.exec_immediate(conn, grant_sql) + print(f"āœ… Executed: {grant_sql}") + except Exception as e: + print(f"āš ļø Error executing grant: {str(e)}") + + # Grant permissions to IOT_WRITE role + print("\nāœ… Granting permissions to IOT_WRITE role...") + write_grants = [ + "GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE IOT.IOT_DATA TO ROLE IOT_WRITE", + "GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA IOT TO ROLE IOT_WRITE", + ] + + for grant_sql in write_grants: + try: + ibm_db.exec_immediate(conn, grant_sql) + print(f"āœ… Executed: {grant_sql}") + except Exception as e: + print(f"āš ļø Error executing grant: {str(e)}") + + print("\n" + "="*60) + print("āœ… Role creation and permission grants completed for all applications") + print("="*60) + + # Close connection + ibm_db.close(conn) + + except Exception as e: + print(f"Error: {str(e)}") + sys.exit(1) + EOFPYTHON + # IMPORTANT: Do not make any changes to the "EOFPYTHON" line above (including its indentation) + + echo "" + echo "Executing ${RDS_ROLES_SCRIPT}" + echo "--------------------------------------------------------------------------------" + python ${RDS_ROLES_SCRIPT} || exit $? + + echo "" + echo "RDS role creation job completed successfully" + + restartPolicy: Never + serviceAccountName: "{{ $sa_name }}" + volumes: [] + backoffLimit: 4 +{{- end }} +{{- end }} diff --git a/root-applications/ibm-mas-instance-root/templates/120-db2-databases-app.yaml b/root-applications/ibm-mas-instance-root/templates/120-db2-databases-app.yaml index 77e433dae..31e619167 100644 --- a/root-applications/ibm-mas-instance-root/templates/120-db2-databases-app.yaml +++ b/root-applications/ibm-mas-instance-root/templates/120-db2-databases-app.yaml @@ -4,6 +4,7 @@ For example: {{ $.Values.account.id }} (instead of {{ .Values.account.id }} ) */}} {{- range $i, $value := .Values.ibm_db2u_databases }} +{{- if not (contains "rds" $value.db2_instance_name) }} --- apiVersion: argoproj.io/v1alpha1 kind: Application @@ -85,4 +86,5 @@ spec: kind: Db2uInstance jsonPointers: - /spec/environment/ssl/secretName +{{- end }} {{- end }} \ No newline at end of file diff --git a/root-applications/ibm-mas-instance-root/templates/120-dbs-rds-databases-app.yaml b/root-applications/ibm-mas-instance-root/templates/120-dbs-rds-databases-app.yaml index 5a273ac52..166909161 100644 --- a/root-applications/ibm-mas-instance-root/templates/120-dbs-rds-databases-app.yaml +++ b/root-applications/ibm-mas-instance-root/templates/120-dbs-rds-databases-app.yaml @@ -3,7 +3,8 @@ For example: {{ $.Values.account.id }} (instead of {{ .Values.account.id }} ) */}} - {{- range $i, $value := .Values.ibm_dbs_rds_databases }} +{{- range $i, $value := .Values.ibm_dbs_rds_databases }} +{{- if contains "rds" $value.db2_instance_name }} --- apiVersion: argoproj.io/v1alpha1 kind: Application @@ -38,12 +39,58 @@ spec: env: - name: {{ $.Values.avp.values_varname }} value: | + # Basic connection configuration rds_admin_db_name: "{{ $value.rds_admin_db_name }}" host: "{{ $value.host }}" port: "{{ $value.port }}" dbname: "{{ $value.dbname }}" user: "{{ $value.user }}" password: "{{ $value.password }}" + + # Additional basic values + db2_namespace: "{{ $value.db2_namespace }}" + {{- if $value.mas_application_id }} + mas_application_id: "{{ $value.mas_application_id }}" + {{- end }} + {{- if $value.jdbc_connection_url }} + jdbc_connection_url: "{{ $value.jdbc_connection_url }}" + {{- end }} + {{- if hasKey $value "replica_db" }} + replica_db: {{ $value.replica_db }} + {{- end }} + + # DB2 Instance Registry Configuration + {{- if $value.db2_instance_registry }} + db2_instance_registry: + {{- range $key, $val := $value.db2_instance_registry }} + {{ $key }}: "{{ $val }}" + {{- end }} + {{- end }} + + # DB2 Database Configuration + {{- if $value.db2_database_db_config }} + db2_database_db_config: + {{- range $key, $val := $value.db2_database_db_config }} + {{ $key }}: "{{ $val }}" + {{- end }} + {{- end }} + + # DB2 Audit Configuration + {{- if $value.db2_addons_audit_config }} + db2_addons_audit_config: + enableAudit: {{ $value.db2_addons_audit_config.enableAudit }} + applyDefaultPolicy: {{ $value.db2_addons_audit_config.applyDefaultPolicy }} + {{- end }} + + # DB2 Instance DBM Configuration + {{- if $value.db2_instance_dbm_config }} + db2_instance_dbm_config: + {{- range $key, $val := $value.db2_instance_dbm_config }} + {{ $key }}: "{{ $val }}" + {{- end }} + {{- end }} + + # JUnit Reporter Configuration junitreporter: reporter_name: {{ $value.db2_instance_name }} cluster_id: "{{ $.Values.cluster.id }}" @@ -66,4 +113,5 @@ spec: syncOptions: - CreateNamespace=false - RespectIgnoreDifferences=true - {{- end }} \ No newline at end of file +{{- end }} +{{- end }} \ No newline at end of file diff --git a/root-applications/ibm-mas-instance-root/templates/510-550-ibm-mas-masapp-configs.yaml b/root-applications/ibm-mas-instance-root/templates/510-550-ibm-mas-masapp-configs.yaml index 63c34977d..00ac53fb3 100644 --- a/root-applications/ibm-mas-instance-root/templates/510-550-ibm-mas-masapp-configs.yaml +++ b/root-applications/ibm-mas-instance-root/templates/510-550-ibm-mas-masapp-configs.yaml @@ -135,6 +135,51 @@ spec: {{- end }} {{- end }} {{- end }} + {{- if not (empty $.Values.ibm_dbs_rds_databases) }} + mas_rds_databases: + {{- range $j, $db := $.Values.ibm_dbs_rds_databases }} + {{- if eq $db.mas_application_id "manage" }} + - rds_host: {{ $db.host }} + rds_port: {{ $db.port | quote }} + rds_dbname: {{ $db.dbname }} + rds_instance_name: {{ $db.db2_instance_name }} + rds_admin_user: {{ $db.user }} + rds_admin_password: {{ $db.password }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + + {{- if (eq $value.mas_app_id "facilities") }} + {{- if not (empty $.Values.ibm_dbs_rds_databases) }} + mas_rds_databases: + {{- range $j, $db := $.Values.ibm_dbs_rds_databases }} + {{- if eq $db.mas_application_id "facilities" }} + - rds_host: {{ $db.host }} + rds_port: {{ $db.port | quote }} + rds_dbname: {{ $db.dbname }} + rds_instance_name: {{ $db.db2_instance_name }} + rds_admin_user: {{ $db.user }} + rds_admin_password: {{ $db.password }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + + {{- if (eq $value.mas_app_id "iot") }} + {{- if not (empty $.Values.ibm_dbs_rds_databases) }} + mas_rds_databases: + {{- range $j, $db := $.Values.ibm_dbs_rds_databases }} + {{- if eq $db.mas_application_id "iot" }} + - rds_host: {{ $db.host }} + rds_port: {{ $db.port | quote }} + rds_dbname: {{ $db.dbname }} + rds_instance_name: {{ $db.db2_instance_name }} + rds_admin_user: {{ $db.user }} + rds_admin_password: {{ $db.password }} + {{- end }} + {{- end }} + {{- end }} {{- end }} {{- if $value.storage_class_definitions }}