--- # Install CloudNativePG via OLM and provision the PostgreSQL Cluster + Database CRs # and credential Secrets that back Automation Orchestrator (backend + Temporal + # Temporal Visibility databases, one shared owner role). # # WARNING: this is a single-instance (no HA), operator-provisioned PostgreSQL # cluster intended for demo/dev use. For production, point automation_orchestrator_operator # at a customer-managed PostgreSQL instance instead of running this role. # # Modeled on https://github.com/ansible/aapctl (internal/install/cloudnativepg). # CloudNativePG's OLM subscription uses Manual install-plan approval, so this role # approves the InstallPlan itself rather than relying on Automatic. # # All tasks are idempotent (kubernetes.core.k8s state: present). - name: Resolve postgres credentials from Vault ansible.builtin.include_tasks: vault_secrets.yml # ------------------------------------------------------------------ # Step 1: Install CloudNativePG operator via OLM # ------------------------------------------------------------------ - name: Create CloudNativePG operator namespace kubernetes.core.k8s: state: present definition: apiVersion: v1 kind: Namespace metadata: name: "{{ cloudnativepg_operator_operator_namespace }}" - name: Create CloudNativePG OperatorGroup kubernetes.core.k8s: state: present definition: apiVersion: operators.coreos.com/v1 kind: OperatorGroup metadata: name: "{{ cloudnativepg_operator_name }}" namespace: "{{ cloudnativepg_operator_operator_namespace }}" spec: {} - name: Subscribe to CloudNativePG operator kubernetes.core.k8s: state: present definition: apiVersion: operators.coreos.com/v1alpha1 kind: Subscription metadata: name: "{{ cloudnativepg_operator_name }}" namespace: "{{ cloudnativepg_operator_operator_namespace }}" spec: channel: "{{ cloudnativepg_operator_channel }}" installPlanApproval: Manual name: "{{ cloudnativepg_operator_name }}" source: "{{ cloudnativepg_operator_catalog_source }}" sourceNamespace: openshift-marketplace # ------------------------------------------------------------------ # Step 2: Approve the Manual InstallPlan and wait for the CSV to succeed # ------------------------------------------------------------------ - name: Wait for OLM to create an InstallPlan for the Subscription kubernetes.core.k8s_info: api_version: operators.coreos.com/v1alpha1 kind: Subscription namespace: "{{ cloudnativepg_operator_operator_namespace }}" name: "{{ cloudnativepg_operator_name }}" register: __cloudnativepg_operator_sub until: >- __cloudnativepg_operator_sub.resources | length > 0 and (__cloudnativepg_operator_sub.resources[0].status.installPlanRef.name | default('')) | length > 0 retries: "{{ __cloudnativepg_operator_wait_retries }}" delay: 10 - name: Read the InstallPlan kubernetes.core.k8s_info: api_version: operators.coreos.com/v1alpha1 kind: InstallPlan namespace: "{{ cloudnativepg_operator_operator_namespace }}" name: "{{ __cloudnativepg_operator_sub.resources[0].status.installPlanRef.name }}" register: __cloudnativepg_operator_installplan - name: Approve the InstallPlan kubernetes.core.k8s_json_patch: api_version: operators.coreos.com/v1alpha1 kind: InstallPlan namespace: "{{ cloudnativepg_operator_operator_namespace }}" name: "{{ __cloudnativepg_operator_sub.resources[0].status.installPlanRef.name }}" patch: - op: add path: /spec/approved value: true - name: Wait for CloudNativePG CSV to succeed kubernetes.core.k8s_info: api_version: operators.coreos.com/v1alpha1 kind: ClusterServiceVersion namespace: "{{ cloudnativepg_operator_operator_namespace }}" name: "{{ __cloudnativepg_operator_installplan.resources[0].spec.clusterServiceVersionNames[0] }}" register: __cloudnativepg_operator_csv until: >- __cloudnativepg_operator_csv.resources | length > 0 and __cloudnativepg_operator_csv.resources[0].status.phase | default('') == 'Succeeded' retries: "{{ __cloudnativepg_operator_wait_retries }}" delay: 10 - name: Wait for CloudNativePG controller manager webhook to be reachable kubernetes.core.k8s_info: api_version: v1 kind: Endpoints namespace: "{{ cloudnativepg_operator_operator_namespace }}" name: cnpg-controller-manager-service register: __cloudnativepg_operator_endpoints until: >- __cloudnativepg_operator_endpoints.resources | length > 0 and (__cloudnativepg_operator_endpoints.resources[0].subsets | default([]) | map(attribute='addresses', default=[]) | flatten | length) > 0 retries: "{{ __cloudnativepg_operator_wait_retries }}" delay: 10 # ------------------------------------------------------------------ # Step 3: Credential secrets (type kubernetes.io/basic-auth) # ------------------------------------------------------------------ - name: Create cluster namespace kubernetes.core.k8s: state: present definition: apiVersion: v1 kind: Namespace metadata: name: "{{ cloudnativepg_operator_cluster_namespace }}" - name: Create postgres credential secrets kubernetes.core.k8s: state: present definition: apiVersion: v1 kind: Secret metadata: name: "{{ item.name }}" namespace: "{{ cloudnativepg_operator_cluster_namespace }}" type: kubernetes.io/basic-auth stringData: username: "{{ cloudnativepg_operator_owner_user }}" password: "{{ __cloudnativepg_operator_password }}" host: "{{ cloudnativepg_operator_cluster_name }}-rw.{{ cloudnativepg_operator_cluster_namespace }}.svc" port: "5432" database: "{{ item.database }}" loop: "{{ cloudnativepg_operator_secrets }}" loop_control: label: "{{ item.name }}" no_log: "{{ cloudnativepg_operator_no_log }}" # ------------------------------------------------------------------ # Step 4: PostgreSQL Cluster # ------------------------------------------------------------------ - name: Create PostgreSQL Cluster kubernetes.core.k8s: state: present definition: apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: "{{ cloudnativepg_operator_cluster_name }}" namespace: "{{ cloudnativepg_operator_cluster_namespace }}" spec: >- {{ { 'instances': cloudnativepg_operator_instances, 'imageName': cloudnativepg_operator_image_name, 'bootstrap': { 'initdb': { 'owner': cloudnativepg_operator_owner_user, 'secret': {'name': cloudnativepg_operator_secrets[0].name}, }, }, 'storage': {'size': cloudnativepg_operator_storage_size}, } | combine({'storage': {'storageClass': cloudnativepg_operator_storage_class}} if cloudnativepg_operator_storage_class else {}, recursive=True) | combine({'postgresql': {'parameters': {'max_connections': cloudnativepg_operator_max_connections | string}}} if cloudnativepg_operator_max_connections else {}, recursive=True) }} # ------------------------------------------------------------------ # Step 5: Logical databases # ------------------------------------------------------------------ - name: Create Database CRs kubernetes.core.k8s: state: present definition: apiVersion: postgresql.cnpg.io/v1 kind: Database metadata: name: "{{ item.name }}" namespace: "{{ cloudnativepg_operator_cluster_namespace }}" spec: name: "{{ item.dbname }}" owner: "{{ cloudnativepg_operator_owner_user }}" cluster: name: "{{ cloudnativepg_operator_cluster_name }}" loop: "{{ cloudnativepg_operator_databases }}" loop_control: label: "{{ item.name }}" - name: Wait for Database CRs to be applied kubernetes.core.k8s_info: api_version: postgresql.cnpg.io/v1 kind: Database namespace: "{{ cloudnativepg_operator_cluster_namespace }}" register: __cloudnativepg_operator_databases_status until: >- __cloudnativepg_operator_databases_status.resources | length >= (cloudnativepg_operator_databases | length) and (__cloudnativepg_operator_databases_status.resources | rejectattr('status.applied', 'undefined') | selectattr('status.applied') | list | length) == (__cloudnativepg_operator_databases_status.resources | length) retries: "{{ __cloudnativepg_operator_wait_retries }}" delay: 10 # ------------------------------------------------------------------ # Step 6: Summary # ------------------------------------------------------------------ - name: Display CloudNativePG deployment summary ansible.builtin.debug: msg: - "CloudNativePG PostgreSQL cluster ready!" - " Operator namespace : {{ cloudnativepg_operator_operator_namespace }}" - " Cluster : {{ cloudnativepg_operator_cluster_name }} ({{ cloudnativepg_operator_cluster_namespace }})" - " Owner role : {{ cloudnativepg_operator_owner_user }}" - " Databases : {{ cloudnativepg_operator_databases | map(attribute='dbname') | join(', ') }}" - " Credential secrets : {{ cloudnativepg_operator_secrets | map(attribute='name') | join(', ') }}"