Add Automation Orchestrator Install and setup

This commit is contained in:
2026-10-02 13:24:44 -04:00
parent 085654d017
commit c164cb2598
23 changed files with 1568 additions and 90 deletions

View File

@@ -0,0 +1,229 @@
---
# 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(', ') }}"

View File

@@ -0,0 +1,26 @@
---
# Read the shared postgres owner password from Vault; generate and store it if missing.
# Sets __cloudnativepg_operator_password, consumed by the K8s secret tasks in main.yml.
- name: Read cloudnativepg postgres password from Vault
community.hashi_vault.vault_kv2_get:
path: "{{ cloudnativepg_operator_vault_path }}"
engine_mount_point: "{{ cloudnativepg_operator_vault_mount }}"
register: __cloudnativepg_operator_vault_read
failed_when: false
no_log: "{{ cloudnativepg_operator_no_log }}"
- name: Resolve postgres password (generate if missing)
ansible.builtin.set_fact:
__cloudnativepg_operator_password: >-
{{ (__cloudnativepg_operator_vault_read.secret | default({})).password
| default(lookup('community.general.random_string', length=32, special=false)) }}
no_log: "{{ cloudnativepg_operator_no_log }}"
- name: Write postgres password to Vault
community.hashi_vault.vault_kv2_write:
path: "{{ cloudnativepg_operator_vault_path }}"
engine_mount_point: "{{ cloudnativepg_operator_vault_mount }}"
data:
password: "{{ __cloudnativepg_operator_password }}"
no_log: "{{ cloudnativepg_operator_no_log }}"