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,50 @@
---
# --- OLM subscription ---
# CloudNativePG is in the certified-operators catalog by default (no custom
# CatalogSource needed, unlike automation_orchestrator_operator).
cloudnativepg_operator_operator_namespace: cloudnative-pg
cloudnativepg_operator_channel: stable-v1
cloudnativepg_operator_name: cloudnative-pg
cloudnativepg_operator_catalog_source: certified-operators
cloudnativepg_operator_wait_timeout: 600
# --- Cluster CR ---
# Namespace for the Cluster, credential Secrets, and Database CRs. Defaults to
# the automation_orchestrator_operator app namespace — keep the two in sync if overridden.
cloudnativepg_operator_cluster_namespace: automation-orchestrator
cloudnativepg_operator_cluster_name: orchestrator-postgres
cloudnativepg_operator_owner_user: orchestrator
cloudnativepg_operator_instances: 1
cloudnativepg_operator_image_name: ghcr.io/cloudnative-pg/postgresql:15
cloudnativepg_operator_storage_size: 5Gi
cloudnativepg_operator_storage_class: ""
cloudnativepg_operator_max_connections: 200
# --- Databases (Database CRs, all owned by cloudnativepg_operator_owner_user) ---
cloudnativepg_operator_databases:
- name: orchestrator
dbname: orchestrator
- name: temporal
dbname: temporal
- name: temporal-visibility
dbname: temporal_visibility
# --- Credential secrets (type kubernetes.io/basic-auth) ---
# All share the same generated username/password; only the referenced database differs.
# secrets[0] is used as the CNPG bootstrap.initdb secret (creates the owner role + first DB).
cloudnativepg_operator_secrets:
- name: orchestrator-postgres-secret
database: orchestrator
- name: temporal-postgres-secret
database: temporal
- name: temporal-visibility-postgres-secret
database: temporal_visibility
# --- Vault ---
# KV2 path/key where the generated postgres password is stored and retrieved.
cloudnativepg_operator_vault_mount: kv
cloudnativepg_operator_vault_path: cloudnativepg/orchestrator-postgres
# --- Debugging ---
# Set to false to disable no_log and expose secret values in task output.
cloudnativepg_operator_no_log: true

View File

@@ -0,0 +1,89 @@
---
argument_specs:
main:
short_description: Install CloudNativePG via OLM and provision a PostgreSQL Cluster + Databases
description:
- Installs the CloudNativePG operator via OLM (certified-operators catalog, Manual install-plan
approval which this role approves itself) and creates a single-instance PostgreSQL Cluster CR
with Database CRs for the orchestrator, temporal, and temporal_visibility databases — all owned
by one shared PostgreSQL role.
- >-
WARNING: this is a demo/dev-oriented, non-HA PostgreSQL deployment. For production, point
automation_orchestrator_operator at a customer-managed PostgreSQL instance instead.
- The generated owner-role password is stored in Vault at cloudnativepg_operator_vault_path
and reused on subsequent runs for idempotency.
options:
cloudnativepg_operator_operator_namespace:
description: Namespace for the CloudNativePG operator.
type: str
default: cloudnative-pg
cloudnativepg_operator_channel:
description: OLM subscription channel.
type: str
default: stable-v1
cloudnativepg_operator_name:
description: Operator package name in the catalog.
type: str
default: cloudnative-pg
cloudnativepg_operator_catalog_source:
description: Name of the CatalogSource to subscribe from.
type: str
default: certified-operators
cloudnativepg_operator_wait_timeout:
description: Seconds to wait for the operator, CSV, and Database CRs to become ready.
type: int
default: 600
cloudnativepg_operator_cluster_namespace:
description: Namespace for the Cluster CR, credential Secrets, and Database CRs.
type: str
default: automation-orchestrator
cloudnativepg_operator_cluster_name:
description: Name of the CloudNativePG Cluster CR.
type: str
default: orchestrator-postgres
cloudnativepg_operator_owner_user:
description: PostgreSQL role that owns all provisioned databases.
type: str
default: orchestrator
cloudnativepg_operator_instances:
description: Number of Postgres instances in the Cluster (1 = no HA).
type: int
default: 1
cloudnativepg_operator_image_name:
description: PostgreSQL container image for the Cluster.
type: str
default: ghcr.io/cloudnative-pg/postgresql:15
cloudnativepg_operator_storage_size:
description: Size of the Cluster's PersistentVolumeClaim.
type: str
default: 5Gi
cloudnativepg_operator_storage_class:
description: StorageClass for the Cluster's PVC. Empty string uses the cluster default.
type: str
default: ""
cloudnativepg_operator_max_connections:
description: postgresql.parameters.max_connections. Set to 0 to omit and use the Postgres default.
type: int
default: 200
cloudnativepg_operator_databases:
description: List of {name, dbname} Database CRs to create, all owned by cloudnativepg_operator_owner_user.
type: list
elements: dict
cloudnativepg_operator_secrets:
description: >-
List of {name, database} credential Secrets to create (type kubernetes.io/basic-auth), all
sharing the same generated username/password. secrets[0] is used as the CNPG bootstrap.initdb secret.
type: list
elements: dict
cloudnativepg_operator_vault_mount:
description: Vault KV2 mount point where the generated owner-role password is stored.
type: str
default: kv
cloudnativepg_operator_vault_path:
description: Vault KV2 path where the generated owner-role password is stored.
type: str
default: cloudnativepg/orchestrator-postgres
cloudnativepg_operator_no_log:
description: Set to false to disable no_log and expose secret values in task output. Never set false in shared environments.
type: bool
default: true

View File

@@ -0,0 +1,18 @@
---
galaxy_info:
author: ptoal
description: Install CloudNativePG via OLM and provision a PostgreSQL Cluster for Automation Orchestrator
license: MIT
min_ansible_version: "2.16"
platforms:
- name: GenericLinux
versions:
- all
galaxy_tags:
- openshift
- postgresql
- cnpg
- operator
- olm
dependencies: []

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 }}"

View File

@@ -0,0 +1,3 @@
---
# Computed internal variables - do not override
__cloudnativepg_operator_wait_retries: "{{ (cloudnativepg_operator_wait_timeout / 10) | int }}"