170 lines
8.6 KiB
YAML
Executable File
170 lines
8.6 KiB
YAML
Executable File
#!/usr/bin/env ansible-playbook
|
|
---
|
|
#==============================================================#
|
|
# File : pgsql-user.yml
|
|
# Desc : create or modify user/role on pgsql cluster
|
|
# Ctime : 2021-02-27
|
|
# Mtime : 2022-12-29
|
|
# Path : pgsql-user.yml
|
|
# Deps : templates/pg-user.sql
|
|
# Docs : https://pigsty.io/docs/pgsql/playbook
|
|
# License : Apache-2.0 @ https://pigsty.io/docs/about/license/
|
|
# Copyright : 2018-2026 Ruohang Feng / Vonng (rh@vonng.com)
|
|
#==============================================================#
|
|
|
|
|
|
#--------------------------------------------------------------#
|
|
# Usage
|
|
#--------------------------------------------------------------#
|
|
#
|
|
# 1. Define new user/role in inventory (cmdb or config)
|
|
# `all.children.<pg_cluster>.vars.pg_users[i]`
|
|
#
|
|
# 2. Execute this playbook on target cluster with arg `username`
|
|
# `pgsql-user.yml -l <pg_cluster> -e username=<name>
|
|
#
|
|
# This playbook will:
|
|
# 1. create user sql definition on `/pg/tmp/pg-user-{{ user.name }}.sql`
|
|
# 2. execute database creation/update sql on cluster leader instance
|
|
# 3. update /etc/pgbouncer/userlist.txt & useropts.txt
|
|
# 4. and reload pgbouncer to take effect
|
|
#
|
|
#--------------------------------------------------------------#
|
|
# Utils
|
|
#--------------------------------------------------------------#
|
|
# Create pgsql user 'username' on pgsql cluster 'cls'
|
|
# bin/pgsql-user <cls> <username>
|
|
# bin/pgsql-user pg-meta dbuser_meta
|
|
#
|
|
#--------------------------------------------------------------#
|
|
# Example
|
|
#--------------------------------------------------------------#
|
|
# pg-meta:
|
|
# vars:
|
|
# pg_users: # define business users/roles on this cluster, array of user definition
|
|
# - name: dbuser_meta # REQUIRED, `name` is the only mandatory field of a user definition
|
|
# state: create # optional, create|absent, create by default. use 'absent' to drop user
|
|
# password: DBUser.Meta # optional, password, can be a scram-sha-256 hash string or plain text
|
|
# login: true # optional, can log in, true by default (new biz ROLE should be false)
|
|
# superuser: false # optional, is superuser? false by default
|
|
# createdb: false # optional, can create database? false by default
|
|
# createrole: false # optional, can create role? false by default
|
|
# inherit: true # optional, can this role use inherited privileges? true by default
|
|
# replication: false # optional, can this role do replication? false by default
|
|
# bypassrls: false # optional, can this role bypass row level security? false by default
|
|
# pgbouncer: true # optional, add this user to pgbouncer user-list? false by default (production user should be true explicitly)
|
|
# connlimit: -1 # optional, user connection limit, default -1 disable limit
|
|
# expire_in: 3650 # optional, now + n days when this role is expired (OVERWRITE expire_at)
|
|
# expire_at: '2030-12-31' # optional, YYYY-MM-DD 'timestamp' when this role is expired (OVERWRITTEN by expire_in)
|
|
# comment: pigsty admin user # optional, comment string for this user/role
|
|
# roles: [dbrole_admin] # optional, belonged roles. default roles are: dbrole_{admin,readonly,readwrite,offline}
|
|
# parameters: {} # optional, role level parameters with `ALTER ROLE SET`
|
|
# pool_mode: transaction # optional, pgbouncer pool mode at user level, transaction by default
|
|
# pool_connlimit: -1 # optional, max database connections at user level, default -1 disable limit
|
|
# search_path: public # key value config parameters according to postgresql documentation (e.g: use pigsty as default search_path)
|
|
#
|
|
# # User with enhanced roles syntax (PG16+ features: set/inherit options)
|
|
# - name: dbuser_test
|
|
# roles:
|
|
# - dbrole_readwrite # simple string: GRANT role
|
|
# - { name: dbrole_admin, admin: true } # grant with ADMIN OPTION (PG16: WITH ADMIN TRUE)
|
|
# - { name: pg_monitor, set: false , inherit: false } # remove set/inherit option
|
|
# - { name: old_role, state: absent } # REVOKE instead of GRANT
|
|
#
|
|
# # One-liner examples
|
|
# - {name: dbuser_view ,password: DBUser.Viewer ,pgbouncer: true ,roles: [dbrole_readonly], comment: read-only viewer for meta database}
|
|
# - {name: dbuser_grafana ,password: DBUser.Grafana ,pgbouncer: true ,roles: [dbrole_admin] ,comment: admin user for grafana database }
|
|
# - {name: dbuser_bytebase ,password: DBUser.Bytebase ,pgbouncer: true ,roles: [dbrole_admin] ,comment: admin user for bytebase database }
|
|
#--------------------------------------------------------------#
|
|
|
|
- name: PGSQL USER
|
|
become: true
|
|
hosts: all
|
|
gather_facts: no
|
|
tasks:
|
|
|
|
#----------------------------------------------------------#
|
|
# Validate username and user definition [preflight]
|
|
#----------------------------------------------------------#
|
|
- name: preflight
|
|
tags: [ preflight, always ]
|
|
connection: local
|
|
block:
|
|
|
|
- name: validate username parameter
|
|
assert:
|
|
that:
|
|
- username is defined
|
|
- username != ''
|
|
- username != 'postgres'
|
|
fail_msg: variable 'username' should be specified (-e username=<name>)
|
|
|
|
- name: fetch user definition
|
|
set_fact: user_def={{ pg_users | json_query(user_def_query) }}
|
|
vars: { user_def_query: "[?name=='{{ username }}'] | [0]" }
|
|
|
|
- name: validate user definition
|
|
assert:
|
|
that:
|
|
- user_def is defined
|
|
- user_def != None
|
|
- user_def != ''
|
|
- user_def != {}
|
|
fail_msg: define user {{ username }} in pg_users first
|
|
|
|
- debug: { msg: "{{ user_def }}" }
|
|
|
|
|
|
#----------------------------------------------------------#
|
|
# Create Postgres User [postgres]
|
|
#----------------------------------------------------------#
|
|
# create user according to user definition
|
|
- include_tasks: roles/pgsql/tasks/user.yml
|
|
tags: postgres
|
|
when: pg_role == 'primary'
|
|
vars: { user: "{{ user_def }}" }
|
|
|
|
# write biz user plain password to .pgpass file for citus cluster
|
|
- name: write plain biz user password to pgpass for citus cluster
|
|
tags: postgres
|
|
become_user: "{{ pg_dbsu|default('postgres') }}"
|
|
when: pg_mode|default('pgsql') == 'citus' and user_def.password is defined and user_def.password != '' and not user_def.password.startswith('md5') and not user_def.password.startswith('scram')
|
|
shell: /bin/bash /pg/bin/pg-pass-add "{{ user_def.name }}" "{{ user_def.password }}"
|
|
args: { executable: /bin/bash }
|
|
|
|
#----------------------------------------------------------#
|
|
# Refresh Pgbouncer User Configuration [pgbouncer]
|
|
#----------------------------------------------------------#
|
|
- name: refresh pgbouncer users
|
|
tags: pgbouncer
|
|
when: user_def.pgbouncer is defined and user_def.pgbouncer|bool
|
|
block:
|
|
|
|
# regenerate user level parameters for pgbouncer if specified
|
|
- name: refresh pgbouncer useropts.txt
|
|
when: user_def.pool_mode is defined or user_def.pool_connlimit is defined
|
|
copy:
|
|
dest: /etc/pgbouncer/useropts.txt
|
|
owner: "{{ pg_dbsu|default('postgres') }}"
|
|
group: postgres
|
|
mode: 0600
|
|
content: |
|
|
# pgbouncer user options
|
|
{% for user in pg_default_roles|default([]) + pg_users|default([]) %}
|
|
{% if (user.state is not defined or user.state != 'absent') and ('pool_mode' in user or 'pool_connlimit' in user) %}
|
|
{{ "%-27s" | format(user.name) }} = {% if 'pool_mode' in user %}pool_mode={{ user.pool_mode }}{% endif %} {% if 'pool_connlimit' in user %}max_user_connections={{ user.pool_connlimit }}{% endif %}
|
|
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
- name: add business users to pgbouncer
|
|
ignore_errors: true
|
|
environment: { PGPORT: "{{ pg_port|default(5432) }}" }
|
|
become_user: "{{ pg_dbsu|default('postgres') }}"
|
|
shell: /bin/bash /pg/bin/pgb-user '{{ username }}' AUTO
|
|
args: { executable: /bin/bash }
|
|
|
|
- name: reload pgbouncer
|
|
systemd: name=pgbouncer state=reloaded enabled=yes daemon_reload=yes
|
|
|
|
... |