## Summary - Add pre-commit framework with hooks for YAML, Ansible, Python, shell, TOML, JSON, and secret detection - Fix all 91+ ansible-lint violations (variable naming, handler capitalization, changed_when) - Fix shellcheck warnings in mise-tasks scripts - Document pre-commit setup in README.md ## Deployment and Testing - [x] All pre-commit hooks pass (`uvx pre-commit run --all-files`) - [x] Test ansible playbook with `--check` mode - [x] Run `mise run indri-services-check` after deploy 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.tail8d86e.ts.net/eblume/blumeops/pulls/19
189 lines
6.3 KiB
YAML
189 lines
6.3 KiB
YAML
---
|
|
# PostgreSQL installation and configuration
|
|
#
|
|
# Passwords are fetched from 1Password at runtime using the `op` CLI.
|
|
# Requires: `op` authenticated on the control machine (run `op signin` first).
|
|
|
|
- name: Install postgresql via homebrew
|
|
community.general.homebrew:
|
|
name: "{{ postgresql_formula }}"
|
|
state: present
|
|
|
|
# === Fetch passwords from 1Password (on control machine) ===
|
|
# These are skipped when running full playbook (pre_tasks sets them)
|
|
# but run when using --tags postgresql
|
|
|
|
- name: Fetch superuser password from 1Password
|
|
ansible.builtin.command:
|
|
cmd: op --vault {{ postgresql_op_vault }} item get {{ postgresql_op_superuser_item }} --fields password --reveal
|
|
delegate_to: localhost
|
|
register: postgresql_superuser_password_result
|
|
changed_when: false
|
|
no_log: true
|
|
check_mode: false
|
|
when: postgresql_superuser_password is not defined
|
|
|
|
- name: Set superuser password fact
|
|
ansible.builtin.set_fact:
|
|
postgresql_superuser_password: "{{ postgresql_superuser_password_result.stdout }}"
|
|
no_log: true
|
|
when: postgresql_superuser_password is not defined
|
|
|
|
- name: Fetch user passwords from 1Password
|
|
ansible.builtin.command:
|
|
cmd: op --vault {{ postgresql_op_vault }} item get {{ item.op_item }} --fields {{ item.op_field }} --reveal
|
|
delegate_to: localhost
|
|
loop: "{{ postgresql_users }}"
|
|
register: postgresql_user_passwords_result
|
|
changed_when: false
|
|
no_log: true
|
|
check_mode: false
|
|
when: postgresql_user_passwords is not defined
|
|
|
|
- name: Build user password lookup
|
|
ansible.builtin.set_fact:
|
|
postgresql_user_passwords: "{{ postgresql_user_passwords | default({}) | combine({item.item.name: item.stdout}) }}"
|
|
loop: "{{ postgresql_user_passwords_result.results }}"
|
|
no_log: true
|
|
when: postgresql_user_passwords is not defined
|
|
|
|
# === Initialize PostgreSQL cluster ===
|
|
|
|
- name: Check if postgresql data directory is initialized
|
|
ansible.builtin.stat:
|
|
path: "{{ postgresql_data_dir }}/PG_VERSION"
|
|
register: postgresql_data_check
|
|
|
|
- name: Create temporary password file for initdb
|
|
ansible.builtin.copy:
|
|
content: "{{ postgresql_superuser_password }}"
|
|
dest: /tmp/.pg_init_pwfile
|
|
mode: '0600'
|
|
when: not postgresql_data_check.stat.exists
|
|
no_log: true
|
|
|
|
- name: Initialize postgresql database cluster with superuser password
|
|
ansible.builtin.command: >
|
|
{{ postgresql_bin_dir }}/initdb
|
|
-U {{ postgresql_superuser }}
|
|
--locale=en_US.UTF-8 -E UTF-8
|
|
--pwfile=/tmp/.pg_init_pwfile
|
|
{{ postgresql_data_dir }}
|
|
when: not postgresql_data_check.stat.exists
|
|
changed_when: true
|
|
|
|
- name: Remove temporary password file
|
|
ansible.builtin.file:
|
|
path: /tmp/.pg_init_pwfile
|
|
state: absent
|
|
when: not postgresql_data_check.stat.exists
|
|
|
|
# === Configure and start PostgreSQL ===
|
|
|
|
- name: Deploy pg_hba.conf
|
|
ansible.builtin.template:
|
|
src: pg_hba.conf.j2
|
|
dest: "{{ postgresql_config_dir }}/pg_hba.conf"
|
|
mode: '0600'
|
|
notify: Restart postgresql
|
|
|
|
- name: Ensure postgresql service is started
|
|
ansible.builtin.command: brew services start {{ postgresql_formula }}
|
|
register: postgresql_brew_start
|
|
changed_when: "'Successfully started' in postgresql_brew_start.stdout"
|
|
failed_when: false
|
|
|
|
- name: Wait for postgresql to accept connections
|
|
ansible.builtin.command: >
|
|
{{ postgresql_bin_dir }}/pg_isready -h localhost -p {{ postgresql_port }}
|
|
register: postgresql_ready
|
|
until: postgresql_ready.rc == 0
|
|
retries: 10
|
|
delay: 2
|
|
changed_when: false
|
|
|
|
# === Create users with passwords ===
|
|
|
|
- name: Check if postgresql users exist
|
|
ansible.builtin.command: >
|
|
{{ postgresql_bin_dir }}/psql -h localhost -U {{ postgresql_superuser }} -d postgres -tAc
|
|
"SELECT 1 FROM pg_roles WHERE rolname = '{{ item.name }}';"
|
|
environment:
|
|
PGPASSWORD: "{{ postgresql_superuser_password }}"
|
|
loop: "{{ postgresql_users }}"
|
|
register: postgresql_user_check
|
|
changed_when: false
|
|
failed_when: false
|
|
no_log: true
|
|
|
|
- name: Create postgresql users with passwords
|
|
ansible.builtin.command: >
|
|
{{ postgresql_bin_dir }}/psql -h localhost -U {{ postgresql_superuser }} -d postgres -c
|
|
"CREATE USER {{ item.item.name }} WITH PASSWORD '{{ postgresql_user_passwords[item.item.name] }}';"
|
|
environment:
|
|
PGPASSWORD: "{{ postgresql_superuser_password }}"
|
|
loop: "{{ postgresql_user_check.results }}"
|
|
when: item.stdout != "1"
|
|
changed_when: true
|
|
no_log: true
|
|
|
|
- name: Update postgresql user passwords (idempotent)
|
|
ansible.builtin.command: >
|
|
{{ postgresql_bin_dir }}/psql -h localhost -U {{ postgresql_superuser }} -d postgres -c
|
|
"ALTER USER {{ item.name }} WITH PASSWORD '{{ postgresql_user_passwords[item.name] }}';"
|
|
environment:
|
|
PGPASSWORD: "{{ postgresql_superuser_password }}"
|
|
loop: "{{ postgresql_users }}"
|
|
changed_when: false
|
|
no_log: true
|
|
|
|
# === Grant roles to users ===
|
|
|
|
- name: Grant roles to users
|
|
ansible.builtin.command: >
|
|
{{ postgresql_bin_dir }}/psql -h localhost -U {{ postgresql_superuser }}
|
|
-d postgres -c "GRANT {{ item.1 }} TO {{ item.0.name }};"
|
|
environment:
|
|
PGPASSWORD: "{{ postgresql_superuser_password }}"
|
|
loop: "{{ postgresql_users | subelements('roles', skip_missing=True) }}"
|
|
changed_when: false
|
|
no_log: true
|
|
|
|
# === Create databases ===
|
|
|
|
- name: Check if postgresql databases exist
|
|
ansible.builtin.command: >
|
|
{{ postgresql_bin_dir }}/psql -h localhost -U {{ postgresql_superuser }} -d postgres -tAc
|
|
"SELECT 1 FROM pg_database WHERE datname = '{{ item.name }}';"
|
|
environment:
|
|
PGPASSWORD: "{{ postgresql_superuser_password }}"
|
|
loop: "{{ postgresql_databases }}"
|
|
register: postgresql_db_check
|
|
changed_when: false
|
|
failed_when: false
|
|
no_log: true
|
|
|
|
- name: Create postgresql databases
|
|
ansible.builtin.command: >
|
|
{{ postgresql_bin_dir }}/createdb -h localhost -U {{ postgresql_superuser }}
|
|
--owner={{ item.item.owner }}
|
|
{{ item.item.name }}
|
|
environment:
|
|
PGPASSWORD: "{{ postgresql_superuser_password }}"
|
|
loop: "{{ postgresql_db_check.results }}"
|
|
when: item.stdout != "1"
|
|
changed_when: true
|
|
no_log: true
|
|
|
|
# === Write credential files for local access ===
|
|
|
|
# .pgpass is used by borgmatic for pg_dump backups
|
|
# Only includes read-only roles (borgmatic has pg_read_all_data)
|
|
- name: Write .pgpass file for borgmatic backups
|
|
ansible.builtin.copy:
|
|
content: |
|
|
# Managed by ansible - only read-only roles
|
|
localhost:{{ postgresql_port }}:*:borgmatic:{{ postgresql_user_passwords['borgmatic'] }}
|
|
dest: ~/.pgpass
|
|
mode: '0600'
|
|
no_log: true
|