diff --git a/README.md b/README.md index 756cd56..dd72194 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ - [nginx](roles/nginx/README.md) - [nginx_docker](roles/nginx_docker/README.md) - [node_exporter](roles/node_exporter/README.md) +- [postgres1c](roles/postgres1c/README.md) - [prometheus](roles/prometheus/README.md) - [promtail](roles/promtail/README.md) - [rclone_yandex](roles/rclone_yandex/README.md) diff --git a/galaxy.yml b/galaxy.yml index a800451..a948bd5 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.29.0 +version: 0.30.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) diff --git a/requirements.yml b/requirements.yml index 7cac208..ce80faa 100644 --- a/requirements.yml +++ b/requirements.yml @@ -7,3 +7,4 @@ collections: - name: community.crypto - name: maxhoesel.borgbackup - name: lucasheld.uptime_kuma + - name: community.postgresql diff --git a/roles/postgres1c/README.md b/roles/postgres1c/README.md new file mode 100644 index 0000000..c3e5177 --- /dev/null +++ b/roles/postgres1c/README.md @@ -0,0 +1,40 @@ +postgres1c +========= + +Installs postgrespro1c (https://1c.postgres.ru/) as a system service. + +Requirements +------------ + +Linux (tested on Ubuntu 22.04) + +Role Variables +-------------- + +`postgres1c_major_ver`: major version of the distribution to be installed +`postgres1c_postgres_pw`: password for user 'postgres' +`postgres1c_postgres_pw_path`: path to the (Ansible-encrypted) YAML file containing a password (see `molecule/default/secrets/`) + +Dependencies +------------ + +- community.postgresql + +Example Playbook +---------------- + +See `molecule/default/coverge.yaml` for an example. + +License +------- + +BSD + +Author Information +------------------ + +Danilkin Danila (MIPT) and Alexander Gorelyshev + +Genlab, LLC + +corvus-migratorius@proton.me diff --git a/roles/postgres1c/defaults/main.yml b/roles/postgres1c/defaults/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/postgres1c/defaults/main.yml @@ -0,0 +1 @@ +--- diff --git a/roles/postgres1c/files/postgres1c_backup.service b/roles/postgres1c/files/postgres1c_backup.service new file mode 100644 index 0000000..efa8453 --- /dev/null +++ b/roles/postgres1c/files/postgres1c_backup.service @@ -0,0 +1,9 @@ +[Unit] +Description=Backup 1C script +Documentation=https://github.com/corvus-migratorius/genlab-ansible + +[Service] +User=postgres +Group=postgres +Type=oneshot +ExecStart=/bin/bash /opt/postgres1c_backup/backup.sh diff --git a/roles/postgres1c/handlers/main.yml b/roles/postgres1c/handlers/main.yml new file mode 100644 index 0000000..a7118c7 --- /dev/null +++ b/roles/postgres1c/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for oneget diff --git a/roles/postgres1c/meta/main.yml b/roles/postgres1c/meta/main.yml new file mode 100644 index 0000000..b69744e --- /dev/null +++ b/roles/postgres1c/meta/main.yml @@ -0,0 +1,14 @@ +--- +galaxy_info: + role_name: postgres1c + namespace: genlab + author: Danila Danilkin + description: Install PostgreSQL (1C version) as a system service + platforms: + - name: "Ubuntu" + versions: ["jammy", "noble"] + company: Genlab, LLC + license: BSD + min_ansible_version: "2.1" + galaxy_tags: [] +dependencies: [] diff --git a/roles/postgres1c/molecule/default/converge.yml b/roles/postgres1c/molecule/default/converge.yml new file mode 100644 index 0000000..a22500b --- /dev/null +++ b/roles/postgres1c/molecule/default/converge.yml @@ -0,0 +1,10 @@ +--- +- name: Converge + hosts: all + roles: + - role: genlab.common.postgres1c + postgres1c_version: 15 + postgres1c_postgres_pw_path: "secrets/postgresql.yaml" + postgres1c_backup_timer: "Sat *-*-* 02:00:00" + postgres1c_backup_compression: "5" + # backup enabled by default at /var/backups/postgres1c/backup_.dump diff --git a/roles/postgres1c/molecule/default/molecule.yml b/roles/postgres1c/molecule/default/molecule.yml new file mode 100644 index 0000000..a2a1072 --- /dev/null +++ b/roles/postgres1c/molecule/default/molecule.yml @@ -0,0 +1,22 @@ +--- +dependency: + name: galaxy +driver: + name: docker +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true +provisioner: + name: ansible +verifier: + name: ansible +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/postgres1c/molecule/default/secrets/postgresql.yaml b/roles/postgres1c/molecule/default/secrets/postgresql.yaml new file mode 100644 index 0000000..976da31 --- /dev/null +++ b/roles/postgres1c/molecule/default/secrets/postgresql.yaml @@ -0,0 +1,2 @@ +--- +postgres1c_postgres_pw: 12345 diff --git a/roles/postgres1c/molecule/default/verify.yml b/roles/postgres1c/molecule/default/verify.yml new file mode 100644 index 0000000..c72a98c --- /dev/null +++ b/roles/postgres1c/molecule/default/verify.yml @@ -0,0 +1,61 @@ +--- +- name: Verify + hosts: all + gather_facts: false + vars: + postgres1c_major_ver: 15 + postgres1c_version: 15 + tasks: + - name: "Include default role vars" + ansible.builtin.include_vars: + dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" + extensions: ['yml'] + + - name: "Get 'postgres' user password" + tags: [postgres1c] + ansible.builtin.include_vars: + file: "secrets/postgresql.yaml" + + - name: "Check that PostgreSQL server responds to the 'postgres' user credentials" + register: postgres1c__verify_db_version + community.postgresql.postgresql_info: + login_host: localhost + login_user: postgres + login_password: "{{ postgres1c_postgres_pw }}" + filter: "version" + + - name: "Verify that correct PostgreSQL version is installed" + ansible.builtin.assert: + that: postgres1c__verify_db_version.version.major == postgres1c_major_ver + success_msg: > + Detected expected PostgreSQL version + '{{ postgres1c__verify_db_version.version.major }}' + ({{ postgres1c__verify_db_version.version }})" + fail_msg: > + Expected PostgreSQL version '{{ postgres1c_major_ver }}'; + Actual '{{ postgres1c__verify_db_version.version.major }}' + ({{ postgres1c__verify_db_version.version.major }})" + + - name: Start service postgrespro-1c (i.e. do nothing and throw fail if it start) + ansible.builtin.service: + name: "postgrespro-1c-{{ postgres1c_version }}" + state: started + register: postgres1c_start_postgrespro1c + failed_when: postgres1c_start_postgrespro1c.changed + + - name: Create a new database with name "test" + tags: [postgres1c] + become: true + become_user: postgres + community.postgresql.postgresql_db: + name: test + comment: "test-db" + login_unix_socket: "/tmp" + + - name: Test backup without cron + tags: [postgres1c] + become: true + become_user: postgres + ansible.builtin.command: /opt/postgres1c_backup/backup.sh + args: + creates: /var/backups/postgres1c/backup_test.tar diff --git a/roles/postgres1c/tasks/backup.yaml b/roles/postgres1c/tasks/backup.yaml new file mode 100644 index 0000000..c004932 --- /dev/null +++ b/roles/postgres1c/tasks/backup.yaml @@ -0,0 +1,40 @@ +- name: "Create opt script directory and backup directory" + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: postgres + group: postgres + mode: "0770" + loop: + - "/var/backups/postgres1c" + - "/opt/postgres1c_backup" + +- name: "Copy backup script" + ansible.builtin.template: + src: "backup.sh.j2" + dest: "/opt/postgres1c_backup/backup.sh" + owner: postgres + group: postgres + mode: "0770" + +- name: "Copy backup service" + ansible.builtin.copy: + src: "postgres1c_backup.service" + dest: "/etc/systemd/system/" + owner: root + group: root + mode: "0770" + +- name: "Copy backup timer" + ansible.builtin.template: + src: "postgres1c_backup.timer.j2" + dest: "/etc/systemd/system/postgres1c_backup.timer" + owner: root + group: root + mode: "0770" + +- name: "Start backup timer" + ansible.builtin.systemd_service: + name: "postgres1c_backup.timer" + state: started + enabled: true diff --git a/roles/postgres1c/tasks/main.yml b/roles/postgres1c/tasks/main.yml new file mode 100644 index 0000000..6f12c2a --- /dev/null +++ b/roles/postgres1c/tasks/main.yml @@ -0,0 +1,88 @@ +--- +- name: "Ensure ACL is installed" + tags: [postgres1c] + ansible.builtin.apt: + name: acl + state: present + update_cache: true + cache_valid_time: 3600 + +- name: "Install PostgreSQL distribution for 1C" + tags: [postgres1c] + block: + - name: "Сheck if already installed" + tags: [postgres1c] + check_mode: true + ansible.builtin.package: + name: "postgrespro-1c-{{ postgres1c_version }}-contrib" + state: present + + rescue: + - name: "Download the offical postgrespro1c installation script" + tags: [postgres1c] + ansible.builtin.get_url: + url: "https://repo.postgrespro.ru/1c/1c-{{ postgres1c_version }}/keys/pgpro-repo-add.sh" + dest: "/root/pgpro-repo-add.sh" + mode: '0755' + + - name: "Add repos using the installation script" + tags: [postgres1c] + changed_when: false + ansible.builtin.command: + cmd: "/root/pgpro-repo-add.sh" + + - name: "Install the 'postgrespro1c' package (and force apt update)" + tags: [postgres1c] + ansible.builtin.apt: + name: "postgrespro-1c-{{ postgres1c_version }}-contrib" + state: present + update_cache: true # we just added new repo, so we need to apt update + + - name: "Ensure the 'ru_RU.UTF-8' locale exists" + tags: [postgres1c] + community.general.locale_gen: + name: ru_RU.UTF-8 + state: present + + - name: "Run initdb" + tags: [postgres1c] + changed_when: false + ansible.builtin.command: + cmd: "/opt/pgpro/1c-{{ postgres1c_version }}/bin/pg-setup initdb --locale ru_RU.UTF-8" + + - name: "Enable postgrespro1c service" + tags: [postgres1c] + changed_when: false + ansible.builtin.command: + cmd: "/opt/pgpro/1c-{{ postgres1c_version }}/bin/pg-setup service enable" + + - name: "Start postgrespro1c service" + tags: [postgres1c] + changed_when: false + ansible.builtin.command: + cmd: "/opt/pgpro/1c-{{ postgres1c_version }}/bin/pg-setup service start" + +- name: "Install psycopg2 (to connect to postgres)" + tags: [postgres1c] + ansible.builtin.package: + name: "python3-psycopg2" + state: present + +- name: "Get 'postgres' user password" + tags: [postgres1c] + ansible.builtin.include_vars: + file: "{{ postgres1c_postgres_pw_path }}" + +- name: "Set password for user 'postgres' via the Unix socket" + tags: [postgres1c] + become: true + become_user: postgres + community.postgresql.postgresql_user: + db: "postgres" + name: "postgres" + password: "{{ postgres1c_postgres_pw }}" + login_unix_socket: "/tmp" + +- name: "Create local postgres1c backup with systemd-timer" + tags: [postgres1c, postgres1c-backup] + ansible.builtin.include_tasks: backup.yaml diff --git a/roles/postgres1c/templates/backup.sh.j2 b/roles/postgres1c/templates/backup.sh.j2 new file mode 100644 index 0000000..6e6e2bb --- /dev/null +++ b/roles/postgres1c/templates/backup.sh.j2 @@ -0,0 +1,35 @@ +#!/bin/bash + +set -eu -o pipefail + +readonly POSTGRES_VER={{ postgres1c_version }} +readonly POSTGRES_COMPRESS={{ postgres1c_backup_compression }} + +echo "Starting 1C backup at $(date +F'%Y-%m-%d %H:%M:%S')" + +# db list array +echo "getting a list of database names via psql ..." +databases=($(/opt/pgpro/1c-${POSTGRES_VER}/bin/psql -t -A -c "SELECT datname FROM pg_database WHERE datname <> ALL ('{template0,template1,postgres}')")) +echo "database names: ${databases[@]}" + +# get current seconds since shell invocation +start_time=$SECONDS + +# iterate over the db list array +for db in "${databases[@]}"; do + start_one_time=$SECONDS + echo "${db} - backup started" + + # local backup command (don't forget to sync to a remote server by borg or some other tool) + # -Fc: "custom format" https://www.postgresql.org/docs/current/app-pgdump.html + # -Z: compression + /opt/pgpro/1c-${POSTGRES_VER}/bin/pg_dump -d ${db} -Fc -Z ${POSTGRES_COMPRESS} -f /var/backups/postgres1c/backup_${db}.dump + + # notify about the db dump completion + echo "${db} - backup done" + total_one_time=$(($SECONDS - start_one_time)) + echo "${db} - total time: ${total_one_time} sec" +done + +total_time=$(($SECONDS - start_time)) +echo "job completed at $(date); total time elapsed: ${total_time} sec" diff --git a/roles/postgres1c/templates/postgres1c_backup.timer.j2 b/roles/postgres1c/templates/postgres1c_backup.timer.j2 new file mode 100644 index 0000000..b8329f0 --- /dev/null +++ b/roles/postgres1c/templates/postgres1c_backup.timer.j2 @@ -0,0 +1,12 @@ +[Unit] +Description=Run backup 1C script. +Documentation=https://github.com/corvus-migratorius/genlab-ansible + +[Timer] +Unit=postgres1c_backup.service +OnCalendar={{ postgres1c_backup_timer }} +Persistent=true +AccuracySec=1min + +[Install] +WantedBy=timers.target