diff --git a/Berksfile b/Berksfile index b2dd09a..5019974 100644 --- a/Berksfile +++ b/Berksfile @@ -24,6 +24,7 @@ cookbook 'composer', '~> 2.7.0' cookbook 'fail2ban', '~> 7.0.4' cookbook 'git', '~> 10.0.0' cookbook 'golang', '~> 5.3.1' +cookbook 'homebrew', '>= 6.0.0' cookbook 'hostname', '= 0.4.2' cookbook 'hostsfile', '~> 3.0.1' cookbook 'java', '~> 4.3.0' diff --git a/Berksfile.lock b/Berksfile.lock index 0bdf0ff..1651b1b 100644 --- a/Berksfile.lock +++ b/Berksfile.lock @@ -8,6 +8,7 @@ DEPENDENCIES firewall (~> 6.2.16) git (~> 10.0.0) golang (~> 5.3.1) + homebrew (>= 6.0.0) hostname (= 0.4.2) hostsfile (~> 3.0.1) ipfs @@ -62,7 +63,7 @@ GRAPH git (10.0.0) golang (5.3.1) ark (>= 6.0) - homebrew (5.4.1) + homebrew (6.0.2) hostname (0.4.2) hostsfile (>= 0.0.0) hostsfile (3.0.1) diff --git a/clients/postgres-11.json b/clients/postgres-11.json new file mode 100644 index 0000000..8ded2ba --- /dev/null +++ b/clients/postgres-11.json @@ -0,0 +1,4 @@ +{ + "name": "postgres-11", + "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1foYpuubS2ovlg3uHO12\nQ/ROZ8MpG+LkCAM46uVfPaoWwfY0vdfMsBOanHDgm9DGUCEBJZ6LPrvCvGXbpPy6\n9GSswK75zVWODblNjvvV4ueGFq4bBFwRuZNjyMlqgyzeU+srZL0ivelu5XEuGuoD\nPYCBKWYqGMz85/eMC7/tinTJtKPyOtXe/G8meji+r7gh3j+ypj/EWeKfcRDa4aGe\n/DmMCurIjjPAXFLMAA6fIqPWVfcPw4APNPE60Z92yPGsTbPu7bL54M5f7udmmu7H\nOgk1HjMAmXCuLDzTkfaxqHP+57yELg/YpXR1E93VmBeQuIBsyOFEk6AmUmA1Ib6e\nnQIDAQAB\n-----END PUBLIC KEY-----\n" +} \ No newline at end of file diff --git a/clients/postgres-12.json b/clients/postgres-12.json new file mode 100644 index 0000000..2ac32cd --- /dev/null +++ b/clients/postgres-12.json @@ -0,0 +1,4 @@ +{ + "name": "postgres-12", + "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1mYGrYB8keUKmXA8dhWc\ncCLzp50xR0ajSw+bWYydyRqD5wuEVKjiJu4+G9QmTVXkVgJ+AYI0Y9/WZYpDqVH6\nvLUo6BSNQaWx20q93qIdOGLy8YG3Qyznezk4l8T9u9vWZDyDpKw6gCxzikMkrXxb\n0cqOYtyud8+PtSEEMogSjOKhRURVHlVrlVH3SQO7Whke9rkiFcbXzubsK9yjkUtF\nxZafSoGorOlDsPvFTfYnkepVB+GHcgiribRYSrO+73GypC2kqMhCpWrb6a0VWsP/\nh53+q3JL3vBvdvjcv51Wpf4n6JdnXnQGn2/MdXEzw+NXgjU4/IdYtbORSbaI8F5t\nowIDAQAB\n-----END PUBLIC KEY-----\n" +} \ No newline at end of file diff --git a/cookbooks/homebrew/.markdownlint-cli2.yaml b/cookbooks/homebrew/.markdownlint-cli2.yaml index 6fa8e77..0196ac7 100644 --- a/cookbooks/homebrew/.markdownlint-cli2.yaml +++ b/cookbooks/homebrew/.markdownlint-cli2.yaml @@ -3,3 +3,5 @@ config: line-length: false # MD013 no-duplicate-heading: false # MD024 reference-links-images: false # MD052 +ignores: + - .github/copilot-instructions.md diff --git a/cookbooks/homebrew/CHANGELOG.md b/cookbooks/homebrew/CHANGELOG.md index d179af1..31f072e 100644 --- a/cookbooks/homebrew/CHANGELOG.md +++ b/cookbooks/homebrew/CHANGELOG.md @@ -2,6 +2,48 @@ This file is used to list changes made in each version of the homebrew cookbook. +## 6.0.2 - *2025-09-04* + +Standardise files with files in sous-chefs/repo-management + +Standardise files with files in sous-chefs/repo-management + +## 6.0.1 - *2025-03-24* + +## 6.0.0 - *2025-03-17* + +- Updated library call for new homebrew class name found in chef-client 18.6.2+ releases + +## 5.4.9 - *2024-11-18* + +Standardise files with files in sous-chefs/repo-management + +Standardise files with files in sous-chefs/repo-management + +Standardise files with files in sous-chefs/repo-management + +Standardise files with files in sous-chefs/repo-management + +Standardise files with files in sous-chefs/repo-management + +## 5.4.8 - *2024-05-07* + +## 5.4.7 - *2024-05-06* + +- Explicitly include `Which` module from `Chef` which fixes runs on 18.x clients. + +## 5.4.6 - *2024-05-06* + +## 5.4.5 - *2023-11-01* + +Standardise files with files in sous-chefs/repo-management + +## 5.4.4 - *2023-09-28* + +## 5.4.3 - *2023-09-04* + +## 5.4.2 - *2023-07-10* + ## 5.4.1 - *2023-06-01* ## 5.4.0 - *2023-04-24* diff --git a/cookbooks/homebrew/libraries/helpers.rb b/cookbooks/homebrew/libraries/helpers.rb index 1699163..f958166 100644 --- a/cookbooks/homebrew/libraries/helpers.rb +++ b/cookbooks/homebrew/libraries/helpers.rb @@ -20,8 +20,9 @@ # class HomebrewUserWrapper - require 'chef/mixin/homebrew_user' - include Chef::Mixin::HomebrewUser + require 'chef/mixin/homebrew' + include Chef::Mixin::Homebrew + include Chef::Mixin::Which end module Homebrew @@ -59,41 +60,17 @@ module Homebrew def owner @owner ||= begin - # once we only support 14.0 we can switch this to find_homebrew_username - require 'etc' - ::Etc.getpwuid(HomebrewUserWrapper.new.find_homebrew_uid).name - rescue Chef::Exceptions::CannotDetermineHomebrewOwner - calculate_owner - end.tap do |owner| - Chef::Log.debug("Homebrew owner is #{owner}") - end - end - - private - - def calculate_owner - owner = homebrew_owner_attr || sudo_user || current_user - if owner == 'root' - raise Chef::Exceptions::User, - "Homebrew owner is 'root' which is not supported. " \ - "To set an explicit owner, please set node['homebrew']['owner']." - end - owner - end - - def homebrew_owner_attr - Chef.node['homebrew']['owner'] - end - - def sudo_user - ENV['SUDO_USER'] - end - - def current_user - ENV['USER'] + HomebrewUserWrapper.new.find_homebrew_username + rescue + Chef::Exceptions::CannotDetermineHomebrewPath + end.tap do |owner| + Chef::Log.debug("Homebrew owner is #{owner}") + end end end unless defined?(Homebrew) class HomebrewWrapper include Homebrew end + +Chef::Mixin::Homebrew.include(Homebrew) diff --git a/cookbooks/homebrew/metadata.json b/cookbooks/homebrew/metadata.json index 8ea519a..23c78ed 100644 --- a/cookbooks/homebrew/metadata.json +++ b/cookbooks/homebrew/metadata.json @@ -17,13 +17,13 @@ "recipes": { }, - "version": "5.4.1", + "version": "6.0.2", "source_url": "https://github.com/sous-chefs/homebrew", "issues_url": "https://github.com/sous-chefs/homebrew/issues", "privacy": false, "chef_versions": [ [ - ">= 15.3" + ">= 18.6.2" ] ], "ohai_versions": [ diff --git a/cookbooks/homebrew/metadata.rb b/cookbooks/homebrew/metadata.rb index 9af47f4..204cca8 100644 --- a/cookbooks/homebrew/metadata.rb +++ b/cookbooks/homebrew/metadata.rb @@ -3,9 +3,9 @@ maintainer 'Sous Chefs' maintainer_email 'help@sous-chefs.org' license 'Apache-2.0' description 'Install Homebrew and includes resources for working with taps and casks' -version '5.4.1' +version '6.0.2' supports 'mac_os_x' source_url 'https://github.com/sous-chefs/homebrew' issues_url 'https://github.com/sous-chefs/homebrew/issues' -chef_version '>= 15.3' +chef_version '>= 18.6.2' diff --git a/cookbooks/homebrew/renovate.json b/cookbooks/homebrew/renovate.json index 7e7a8ba..a0b29c8 100644 --- a/cookbooks/homebrew/renovate.json +++ b/cookbooks/homebrew/renovate.json @@ -1,9 +1,10 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:base"], - "packageRules": [{ + "packageRules": [ + { "groupName": "Actions", - "matchUpdateTypes": ["patch", "pin", "digest"], + "matchUpdateTypes": ["minor", "patch", "pin"], "automerge": true, "addLabels": ["Release: Patch", "Skip: Announcements"] }, diff --git a/cookbooks/homebrew/resources/cask.rb b/cookbooks/homebrew/resources/cask.rb index c4d2053..f66fc8f 100644 --- a/cookbooks/homebrew/resources/cask.rb +++ b/cookbooks/homebrew/resources/cask.rb @@ -19,6 +19,7 @@ # limitations under the License. # +unified_mode true chef_version_for_provides '< 14.0' if respond_to?(:chef_version_for_provides) property :cask_name, String, regex: %r{^[\w/-]+$}, name_property: true diff --git a/cookbooks/homebrew/resources/tap.rb b/cookbooks/homebrew/resources/tap.rb index 8f67124..dc7f329 100644 --- a/cookbooks/homebrew/resources/tap.rb +++ b/cookbooks/homebrew/resources/tap.rb @@ -19,6 +19,7 @@ # limitations under the License. # +unified_mode true chef_version_for_provides '< 14.0' if respond_to?(:chef_version_for_provides) property :tap_name, String, name_property: true, regex: %r{^[\w-]+(?:\/[\w-]+)+$} diff --git a/doc/postgres/migration.md b/doc/postgres/migration.md new file mode 100644 index 0000000..27915a5 --- /dev/null +++ b/doc/postgres/migration.md @@ -0,0 +1,287 @@ +# Migrating PostgreSQL cluster to a new major version + +## Summary + +1. Dump from a replica +2. Restore to fresh VM running new major version +3. Add logical replication for delta sync from current/old primary +4. Switch primary to new server +5. Remove logical replication on new server + +## Runbook + +* Primary host: `PRIMARY_HOST` +* Replica host: `REPLICA_HOST` +* New PG14 host: `NEW_HOST` +* PostgreSQL superuser: `postgres` +* Running locally on each machine via `sudo -u postgres` + +Adjust hostnames/IPs/etc. where needed. + +--- + +### ๐ŸŸข 0. PRIMARY โ€” Pre-checks + +```bash +sudo -u postgres psql -c "SHOW wal_level;" +sudo -u postgres psql -c "SHOW max_replication_slots;" +``` + +If needed, edit config: + +```bash +sudo -u postgres vi $PGDATA/postgresql.conf +``` + +Ensure: + +```conf +wal_level = logical +max_replication_slots = 10 +``` + +Restart if changed: + +```bash +sudo systemctl restart postgresql +``` + +--- + +### ๐Ÿ”ต๐ŸŸก 3. Create keypair for syncing dump later + +๐Ÿ”ต On NEW_HOST: + +```bash +sudo mkdir -p /home/postgres/.ssh && \ +sudo chown -R postgres:postgres /home/postgres && \ +sudo chmod 700 /home/postgres/.ssh && \ +sudo -u postgres bash -c 'ssh-keygen -t ecdsa -b 256 -f /home/postgres/.ssh/id_ecdsa -N "" -C "postgres@$(hostname)"' && \ +sudo cat /home/postgres/.ssh/id_ecdsa.pub +``` + +Copy the public key from the above output + +๐ŸŸก On replica: + +```bash +sudo mkdir -p /home/postgres/.ssh && \ +sudo chown -R postgres:postgres /home/postgres && \ +sudo chmod 700 /home/postgres/.ssh && \ +echo [public_key] | sudo tee /home/postgres/.ssh/authorized_keys > /dev/null && \ +sudo chmod 700 /home/postgres/.ssh +``` + +--- + +### ๐ŸŸข 1. PRIMARY โ€” Create publication and replication slots + +```bash +sudo -u postgres pg_create_replication_publications +``` + +or + +```bash +sudo -u postgres pg_create_replication_publication [db_name] +``` + +Listing publications and slots: + +```bash +sudo -u postgres pg_list_replication_publications +sudo -u postgres pg_list_replication_slots +``` + +--- + +### ๐ŸŸก 3. REPLICA โ€” Pause replication + +```bash +sudo -u postgres psql -c "SELECT pg_wal_replay_pause();" +``` + +Verify: + +```bash +sudo -u postgres psql -c "SELECT pg_is_wal_replay_paused();" +``` + +--- + +### ๐ŸŸก 4. REPLICA โ€” Run dump + +```bash +sudo -u postgres pg_dump_all_databases +``` + +or + +```bash +sudo -u postgres bash -c "pg_dumpall --globals-only > /tmp/globals.sql" +sudo -u postgres pg_dump_database [db_name] +``` + +--- + +### ๐ŸŸก 5. REPLICA โ€” Resume replication + +```bash +sudo -u postgres psql -c "SELECT pg_wal_replay_resume();" +``` + +--- + +### ๐Ÿ”ต 6. COPY dumps to NEW HOST + +From NEW_HOST: + +```bash +export REPLICA_HOST=[private_ip] && \ +cd /tmp && \ +sudo -u postgres scp "postgres@$REPLICA_HOST:/tmp/globals.sql" . && \ +sudo -u postgres scp "postgres@$REPLICA_HOST:/tmp/dump_*.tar.zst" . +``` + +--- + +### ๐Ÿ”ต 7. NEW HOST (PostgreSQL 14) โ€” Restore + +#### 7.1 Restore globals + +```bash +sudo -u postgres psql -f /tmp/globals.sql +``` + +--- + +#### 7.2 Create databases + +```bash +sudo -u postgres psql -Atqc "SELECT datname FROM pg_database WHERE datallowconn AND datname NOT IN ('template1')" | \ +xargs -I{} sudo -u postgres createdb {} +``` + +or + +```bash +sudo -u postgres createdb [db_name] +``` + +--- + +#### 7.3 Restore each database + +```bash +sudo -u postgres pg_restore_all_databases +``` + +or + +```bash +sudo -u postgres pg_restore_database [db_name] +``` + +--- + +### ๐Ÿ”ต 8. NEW HOST โ€” Create subscriptions + +```bash +sudo -u postgres pg_create_replication_subscriptions +``` + +or + +```bash +sudo -u postgres pg_create_replication_subscription [db_name] +``` + +--- + +### ๐Ÿ”ต 9. NEW HOST โ€” Monitor replication + +```bash +sudo -u postgres pg_list_replication_subscriptions +``` + +--- + +### ๐Ÿ”ด 11. CUTOVER + +#### 11.1 Stop writes on old primary + +Put app(s) in maintenance mode, stop the app/daemons. + +--- + +#### 11.2 Wait for replication to catch up + +TODO: not the best way to check, since WAL LSNs keep increasing + +```bash +sudo -u postgres psql -d [db_name] -c "SELECT * FROM pg_stat_subscription;" +``` + +--- + +#### 11.3 Fix sequences + +Run per DB: + +```bash +sudo -u postgres pg_fix_sequences_in_all_databases +``` + +or + +```bash +sudo -u postgres pg_fix_sequences [db_name] +``` + +--- + +#### 11.4 Point app to NEW_HOST + +1. Update `pg.kosmos.local` in `/etc/hosts` on app server(s). For example: + + ```bash + export NEW_PG_PRIMARY=[private_ip] + knife ssh roles:ejabberd -a knife_zero.host "sudo sed -r \"s/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s(pg.kosmos.local)/$NEW_PG_PRIMARY\t\1/\" -i /etc/hosts" + ``` + + Or override node attribute(s) if necessary and/or approporiate. + +2. Start the app/daemons, and deactivate maintenance mode. + +--- + +### ๐Ÿงน 12. CLEANUP NEW_HOST + +```bash +sudo -u postgres pg_drop_replication_subscriptions +``` + +--- + +### ๐Ÿงน 13. CLEANUP PRIMARY + +TODO: Looks like slots are dropped automatically, when subscriptions are dropped + +```bash +sudo -u postgres pg_drop_replication_publications +``` + +--- + +### ๐Ÿงน 13. CLEANUP Chef + +Once all apps/databases are migrated, update the role in the node +config of the new primary to 'postgres_primary' and converge it. + +Also delete the old primary node config from the Chef repo. + +--- + +### โœ… DONE + +--- diff --git a/nodes/postgres-6.json b/nodes/postgres-11.json similarity index 74% rename from nodes/postgres-6.json rename to nodes/postgres-11.json index 59d1cdc..a01909f 100644 --- a/nodes/postgres-6.json +++ b/nodes/postgres-11.json @@ -1,16 +1,17 @@ { - "name": "postgres-6", + "name": "postgres-11", + "chef_environment": "production", "normal": { "knife_zero": { - "host": "10.1.1.196" + "host": "10.1.1.91" } }, "automatic": { - "fqdn": "postgres-6", + "fqdn": "postgres-11", "os": "linux", - "os_version": "5.4.0-173-generic", - "hostname": "postgres-6", - "ipaddress": "192.168.122.60", + "os_version": "5.15.0-1095-kvm", + "hostname": "postgres-11", + "ipaddress": "192.168.122.142", "roles": [ "base", "kvm_guest", @@ -21,18 +22,20 @@ "kosmos-base::default", "kosmos_kvm::guest", "kosmos_postgresql::primary", - "kosmos_postgresql::firewall", "kosmos-akkounts::pg_db", "kosmos-bitcoin::lndhub-go_pg_db", "kosmos-bitcoin::nbxplorer_pg_db", "kosmos_drone::pg_db", "kosmos_gitea::pg_db", "kosmos-mastodon::pg_db", + "kosmos_postgresql::firewall", + "kosmos_postgresql::management_scripts", "apt::default", "timezone_iii::default", "timezone_iii::debian", "ntp::default", "ntp::apparmor", + "kosmos-base::journald_conf", "kosmos-base::systemd_emails", "apt::unattended-upgrades", "kosmos-base::firewall", @@ -44,17 +47,17 @@ "hostname::default" ], "platform": "ubuntu", - "platform_version": "20.04", + "platform_version": "22.04", "cloud": null, "chef_packages": { "chef": { - "version": "18.4.2", - "chef_root": "/opt/chef/embedded/lib/ruby/gems/3.1.0/gems/chef-18.4.2/lib", + "version": "18.10.17", + "chef_root": "/opt/chef/embedded/lib/ruby/gems/3.1.0/gems/chef-18.10.17/lib", "chef_effortless": null }, "ohai": { - "version": "18.1.11", - "ohai_root": "/opt/chef/embedded/lib/ruby/gems/3.1.0/gems/ohai-18.1.11/lib/ohai" + "version": "18.2.13", + "ohai_root": "/opt/chef/embedded/lib/ruby/gems/3.1.0/gems/ohai-18.2.13/lib/ohai" } } }, diff --git a/nodes/postgres-10.json b/nodes/postgres-12.json similarity index 85% rename from nodes/postgres-10.json rename to nodes/postgres-12.json index 73cde74..d2359b2 100644 --- a/nodes/postgres-10.json +++ b/nodes/postgres-12.json @@ -1,17 +1,17 @@ { - "name": "postgres-10", + "name": "postgres-12", "chef_environment": "production", "normal": { "knife_zero": { - "host": "10.1.1.176" + "host": "10.1.1.134" } }, "automatic": { - "fqdn": "postgres-10", + "fqdn": "postgres-12", "os": "linux", - "os_version": "5.15.0-1095-kvm", - "hostname": "postgres-10", - "ipaddress": "192.168.122.41", + "os_version": "5.15.0-1096-kvm", + "hostname": "postgres-12", + "ipaddress": "192.168.122.139", "roles": [ "base", "kvm_guest", @@ -24,6 +24,7 @@ "kosmos_postgresql::hostsfile", "kosmos_postgresql::replica", "kosmos_postgresql::firewall", + "kosmos_postgresql::management_scripts", "apt::default", "timezone_iii::default", "timezone_iii::debian", diff --git a/nodes/postgres-8.json b/nodes/postgres-8.json deleted file mode 100644 index 82d4395..0000000 --- a/nodes/postgres-8.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "postgres-8", - "chef_environment": "production", - "normal": { - "knife_zero": { - "host": "10.1.1.99" - } - }, - "automatic": { - "fqdn": "postgres-8", - "os": "linux", - "os_version": "5.15.0-1059-kvm", - "hostname": "postgres-8", - "ipaddress": "192.168.122.100", - "roles": [ - "base", - "kvm_guest", - "postgresql_replica" - ], - "recipes": [ - "kosmos-base", - "kosmos-base::default", - "kosmos_kvm::guest", - "kosmos_postgresql::hostsfile", - "kosmos_postgresql::replica", - "kosmos_postgresql::firewall", - "apt::default", - "timezone_iii::default", - "timezone_iii::debian", - "ntp::default", - "ntp::apparmor", - "kosmos-base::systemd_emails", - "apt::unattended-upgrades", - "kosmos-base::firewall", - "kosmos-postfix::default", - "postfix::default", - "postfix::_common", - "postfix::_attributes", - "postfix::sasl_auth", - "hostname::default" - ], - "platform": "ubuntu", - "platform_version": "22.04", - "cloud": null, - "chef_packages": { - "chef": { - "version": "18.5.0", - "chef_root": "/opt/chef/embedded/lib/ruby/gems/3.1.0/gems/chef-18.5.0/lib", - "chef_effortless": null - }, - "ohai": { - "version": "18.1.11", - "ohai_root": "/opt/chef/embedded/lib/ruby/gems/3.1.0/gems/ohai-18.1.11/lib/ohai" - } - } - }, - "run_list": [ - "role[base]", - "role[kvm_guest]", - "role[postgresql_replica]" - ] -} diff --git a/nodes/postgres-9.json b/nodes/postgres-9.json deleted file mode 100644 index 529052f..0000000 --- a/nodes/postgres-9.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "name": "postgres-9", - "chef_environment": "production", - "normal": { - "knife_zero": { - "host": "10.1.1.3" - } - }, - "automatic": { - "fqdn": "postgres-9", - "os": "linux", - "os_version": "5.15.0-1059-kvm", - "hostname": "postgres-9", - "ipaddress": "192.168.122.64", - "roles": [ - "base", - "kvm_guest", - "postgresql_replica" - ], - "recipes": [ - "kosmos-base", - "kosmos-base::default", - "kosmos_kvm::guest", - "kosmos_postgresql::hostsfile", - "kosmos_postgresql::replica", - "kosmos_postgresql::firewall", - "apt::default", - "timezone_iii::default", - "timezone_iii::debian", - "ntp::default", - "ntp::apparmor", - "kosmos-base::journald_conf", - "kosmos-base::systemd_emails", - "apt::unattended-upgrades", - "kosmos-base::firewall", - "kosmos-postfix::default", - "postfix::default", - "postfix::_common", - "postfix::_attributes", - "postfix::sasl_auth", - "hostname::default" - ], - "platform": "ubuntu", - "platform_version": "22.04", - "cloud": null, - "chef_packages": { - "chef": { - "version": "18.8.54", - "chef_root": "/opt/chef/embedded/lib/ruby/gems/3.1.0/gems/chef-18.8.54/lib", - "chef_effortless": null - }, - "ohai": { - "version": "18.2.8", - "ohai_root": "/opt/chef/embedded/lib/ruby/gems/3.1.0/gems/ohai-18.2.8/lib/ohai" - } - } - }, - "run_list": [ - "role[base]", - "role[kvm_guest]", - "role[postgresql_replica]" - ] -} diff --git a/roles/postgresql_primary.rb b/roles/postgresql_primary.rb index a4fb4b8..27e1dde 100644 --- a/roles/postgresql_primary.rb +++ b/roles/postgresql_primary.rb @@ -1,12 +1,13 @@ name "postgresql_primary" -run_list %w( - kosmos_postgresql::primary - kosmos_postgresql::firewall - kosmos-akkounts::pg_db - kosmos-bitcoin::lndhub-go_pg_db - kosmos-bitcoin::nbxplorer_pg_db - kosmos_drone::pg_db - kosmos_gitea::pg_db - kosmos-mastodon::pg_db -) +run_list [ + "kosmos_postgresql::primary", + "kosmos-akkounts::pg_db", + "kosmos-bitcoin::lndhub-go_pg_db", + "kosmos-bitcoin::nbxplorer_pg_db", + "kosmos_drone::pg_db", + "kosmos_gitea::pg_db", + "kosmos-mastodon::pg_db", + "kosmos_postgresql::firewall", + "kosmos_postgresql::management_scripts" +] diff --git a/roles/postgresql_replica.rb b/roles/postgresql_replica.rb index 099291d..9050352 100644 --- a/roles/postgresql_replica.rb +++ b/roles/postgresql_replica.rb @@ -1,7 +1,8 @@ name "postgresql_replica" -run_list %w( - kosmos_postgresql::hostsfile - kosmos_postgresql::replica - kosmos_postgresql::firewall -) +run_list [ + "kosmos_postgresql::hostsfile", + "kosmos_postgresql::replica", + "kosmos_postgresql::firewall", + "kosmos_postgresql::management_scripts" +] diff --git a/roles/postgresql_replica_logical.rb b/roles/postgresql_replica_logical.rb new file mode 100644 index 0000000..1e1a7c9 --- /dev/null +++ b/roles/postgresql_replica_logical.rb @@ -0,0 +1,8 @@ +name "postgresql_replica_logical" + +run_list [ + "kosmos_postgresql::hostsfile", + "kosmos_postgresql::replica_logical", + "kosmos_postgresql::firewall", + "kosmos_postgresql::management_scripts" +] diff --git a/site-cookbooks/kosmos-akkounts/recipes/default.rb b/site-cookbooks/kosmos-akkounts/recipes/default.rb index 0f9f6d9..f8ec675 100644 --- a/site-cookbooks/kosmos-akkounts/recipes/default.rb +++ b/site-cookbooks/kosmos-akkounts/recipes/default.rb @@ -230,7 +230,6 @@ systemd_unit "akkounts.service" do WorkingDirectory: deploy_path, Environment: "RAILS_ENV=#{rails_env} SOLID_QUEUE_IN_PUMA=true", ExecStart: "#{bundle_path} exec puma -C config/puma.rb --pidfile #{deploy_path}/tmp/puma.pid", - ExecStop: "#{bundle_path} exec puma -C config/puma.rb --pidfile #{deploy_path}/tmp/puma.pid stop", ExecReload: "#{bundle_path} exec pumactl -F config/puma.rb --pidfile #{deploy_path}/tmp/puma.pid phased-restart", PIDFile: "#{deploy_path}/tmp/puma.pid", TimeoutSec: "10", diff --git a/site-cookbooks/kosmos-base/recipes/default.rb b/site-cookbooks/kosmos-base/recipes/default.rb index 666a0d1..7d7b5eb 100644 --- a/site-cookbooks/kosmos-base/recipes/default.rb +++ b/site-cookbooks/kosmos-base/recipes/default.rb @@ -24,11 +24,17 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -include_recipe 'apt' -include_recipe 'timezone_iii' -include_recipe 'kosmos-base::journald_conf' -include_recipe 'kosmos-base::systemd_emails' +include_recipe "apt" + +directory "/etc/apt/keyrings" do + mode "0755" + action :create +end + +include_recipe "timezone_iii" include_recipe "ntp" if node["platform"] == "ubuntu" && node["platform_version"].to_f < 24.04 +include_recipe "kosmos-base::journald_conf" +include_recipe "kosmos-base::systemd_emails" node.override["apt"]["unattended_upgrades"]["enable"] = true node.override["apt"]["unattended_upgrades"]["mail_only_on_error"] = false @@ -43,57 +49,57 @@ node.override["apt"]["unattended_upgrades"]["allowed_origins"] = [ ] node.override["apt"]["unattended_upgrades"]["mail"] = "ops@kosmos.org" node.override["apt"]["unattended_upgrades"]["syslog_enable"] = true -include_recipe 'apt::unattended-upgrades' +include_recipe "apt::unattended-upgrades" -package 'mailutils' -package 'mosh' -package 'vim' +package "mailutils" +package "mosh" +package "vim" # Don't create users and rewrite the sudo config in development environment. # It breaks the vagrant user unless node.chef_environment == "development" # Searches data bag "users" for groups attribute "sysadmin". # Places returned users in Unix group "sysadmin" with GID 2300. - users_manage 'sysadmin' do + users_manage "sysadmin" do group_id 2300 - action [:remove, :create] + action %i[remove create] end sudo "sysadmin" do groups "sysadmin" nopasswd true defaults [ - # not default on Ubuntu, explicitely enable. Uses a minimal white list of - # environment variables - 'env_reset', - # Send emails on unauthorized attempts - 'mail_badpass', - 'secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"', + # not default on Ubuntu, explicitely enable. Uses a minimal white list of + # environment variables + "env_reset", + # Send emails on unauthorized attempts + "mail_badpass", + 'secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"' ] end include_recipe "kosmos-base::firewall" - include_recipe 'kosmos-postfix' + include_recipe "kosmos-postfix" - node.override['set_fqdn'] = '*' - include_recipe 'hostname' + node.override["set_fqdn"] = "*" + include_recipe "hostname" - package 'ca-certificates' + package "ca-certificates" - directory '/usr/local/share/ca-certificates/cacert' do + directory "/usr/local/share/ca-certificates/cacert" do action :create end - ['http://www.cacert.org/certs/root.crt', 'http://www.cacert.org/certs/class3.crt'].each do |cert| + ["http://www.cacert.org/certs/root.crt", "http://www.cacert.org/certs/class3.crt"].each do |cert| remote_file "/usr/local/share/ca-certificates/cacert/#{File.basename(cert)}" do source cert action :create_if_missing - notifies :run, 'execute[update-ca-certificates]', :immediately + notifies :run, "execute[update-ca-certificates]", :immediately end end - execute 'update-ca-certificates' do + execute "update-ca-certificates" do action :nothing end end diff --git a/site-cookbooks/kosmos_drone/attributes/default.rb b/site-cookbooks/kosmos_drone/attributes/default.rb index ceb7564..498ed5e 100644 --- a/site-cookbooks/kosmos_drone/attributes/default.rb +++ b/site-cookbooks/kosmos_drone/attributes/default.rb @@ -1,2 +1,6 @@ node.default["kosmos_drone"]["domain"] = "drone.kosmos.org" node.default["kosmos_drone"]["upstream_port"] = 80 +node.default["kosmos_drone"]["pg_host"] = "pg.kosmos.local" +node.default["kosmos_drone"]["pg_port"] = 5432 +node.default["kosmos_drone"]["pg_db"] = "drone" +node.default["kosmos_drone"]["pg_user"] = "drone" diff --git a/site-cookbooks/kosmos_drone/recipes/default.rb b/site-cookbooks/kosmos_drone/recipes/default.rb index f816377..f480d37 100644 --- a/site-cookbooks/kosmos_drone/recipes/default.rb +++ b/site-cookbooks/kosmos_drone/recipes/default.rb @@ -9,11 +9,11 @@ credentials = data_bag_item("credentials", "drone") drone_credentials = data_bag_item('credentials', 'drone') postgres_config = { - username: "drone", - password: drone_credentials["postgresql_password"], - host: "pg.kosmos.local", - port: 5432, - database: "drone" + host: node["kosmos_drone"]["pg_host"], + port: node["kosmos_drone"]["pg_port"], + database: node["kosmos_drone"]["pg_db"], + username: node["kosmos_drone"]["pg_user"], + password: drone_credentials["postgresql_password"] } directory deploy_path do diff --git a/site-cookbooks/kosmos_postgresql/attributes/default.rb b/site-cookbooks/kosmos_postgresql/attributes/default.rb index f3daf9b..29aae1f 100644 --- a/site-cookbooks/kosmos_postgresql/attributes/default.rb +++ b/site-cookbooks/kosmos_postgresql/attributes/default.rb @@ -1,3 +1,8 @@ +node.default['kosmos_postgresql']['postgresql_version'] = "14" + # This is set to false by default, and set to true in the server resource # for replicas. node.default['kosmos_postgresql']['ready_to_set_up_replica'] = false + +# Address space from which clients are allowed to connect +node.default['kosmos_postgresql']['access_addr'] = "10.1.1.0/24" diff --git a/site-cookbooks/kosmos_postgresql/files/create_publication.sh b/site-cookbooks/kosmos_postgresql/files/create_publication.sh new file mode 100644 index 0000000..2aa7e8f --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/files/create_publication.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -euo pipefail + +DB_NAME="${1:?Usage: $0 }" + +echo "== Processing DB: $DB_NAME ==" + +# Create publication (idempotent) +psql -d "$DB_NAME" -v ON_ERROR_STOP=1 <<'SQL' +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_publication WHERE pubname = 'migrate_pub' + ) THEN + CREATE PUBLICATION migrate_pub FOR ALL TABLES; + END IF; +END +$$; +SQL + +# Create logical replication slot (idempotent-ish) +SLOT="migrate_slot_${DB_NAME}" + +if ! psql -d "$DB_NAME" -Atqc "SELECT 1 FROM pg_replication_slots WHERE slot_name = '$SLOT'" | grep -q 1; then + echo " Creating slot: $SLOT" + psql -d "$DB_NAME" -c "SELECT pg_create_logical_replication_slot('$SLOT', 'pgoutput');" +else + echo " Slot already exists: $SLOT" +fi + +echo "== Done ==" diff --git a/site-cookbooks/kosmos_postgresql/files/create_publications.sh b/site-cookbooks/kosmos_postgresql/files/create_publications.sh new file mode 100644 index 0000000..7b98acc --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/files/create_publications.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -e + +echo "== Creating publication in each database ==" + +for db in $(psql -Atqc "SELECT datname FROM pg_database WHERE datallowconn AND datname NOT IN ('template1','postgres')"); do + echo "Processing DB: $db" + + # Create publication (idempotent) + psql -d "$db" -v ON_ERROR_STOP=1 < globals.sql) && \ +psql -Atqc "SELECT datname FROM pg_database WHERE datallowconn AND datname NOT IN (''template1'',''postgres'')" | \ +xargs -I{} -P4 sh -c " + pg_dump -Fd -j 4 -d \"{}\" -f dump_{} && + tar -cf - dump_{} | zstd -19 -T0 > dump_{}.tar.zst && + rm -rf dump_{} +" diff --git a/site-cookbooks/kosmos_postgresql/files/dump_database.sh b/site-cookbooks/kosmos_postgresql/files/dump_database.sh new file mode 100644 index 0000000..028ad77 --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/files/dump_database.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -euo pipefail + +DB_NAME="${1:?Usage: $0 }" + +cd /tmp + +pg_dump -Fd -j 4 -d "$DB_NAME" -f "dump_${DB_NAME}" +tar -cf - "dump_${DB_NAME}" | zstd -19 -T0 > "dump_${DB_NAME}.tar.zst" +rm -rf "dump_${DB_NAME}" diff --git a/site-cookbooks/kosmos_postgresql/files/fix_sequences.sh b/site-cookbooks/kosmos_postgresql/files/fix_sequences.sh new file mode 100644 index 0000000..a158cdd --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/files/fix_sequences.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -e + +DB="$1" + +if [ -z "$DB" ]; then + echo "Usage: $0 " + exit 1 +fi + +echo "== Fixing sequences in database: $DB ==" + +SQL=$(psql -d "$DB" -Atqc " + SELECT + 'SELECT setval(' || + quote_literal(pg_get_serial_sequence(quote_ident(n.nspname)||'.'||quote_ident(c.relname), a.attname)) || + ', COALESCE(MAX(' || quote_ident(a.attname) || '), 0) + 1, false) FROM ' || + quote_ident(n.nspname)||'.'||quote_ident(c.relname) || ';' + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + JOIN pg_attribute a ON a.attrelid = c.oid + WHERE c.relkind = 'r' + AND a.attnum > 0 + AND NOT a.attisdropped + AND pg_get_serial_sequence(quote_ident(n.nspname)||'.'||quote_ident(c.relname), a.attname) IS NOT NULL; +") + +if [ -z "$SQL" ]; then + echo "No sequences found in $DB" + exit 0 +fi + +echo "$SQL" | psql -d "$DB" + +echo "== Done ==" diff --git a/site-cookbooks/kosmos_postgresql/files/fix_sequences_in_all_databases.sh b/site-cookbooks/kosmos_postgresql/files/fix_sequences_in_all_databases.sh new file mode 100644 index 0000000..f8fc2b2 --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/files/fix_sequences_in_all_databases.sh @@ -0,0 +1,38 @@ +#!/bin/bash +set -e + +echo "== Fixing sequences across all databases ==" + +for db in $(psql -Atqc "SELECT datname FROM pg_database WHERE datallowconn AND datname NOT IN ('template1','postgres')"); do + echo "---- DB: $db ----" + + # Generate fix statements + SQL=$(psql -d "$db" -Atqc " + SELECT + 'SELECT setval(' || + quote_literal(pg_get_serial_sequence(quote_ident(n.nspname)||'.'||quote_ident(c.relname), a.attname)) || + ', COALESCE(MAX(' || quote_ident(a.attname) || '), 0) + 1, false) FROM ' || + quote_ident(n.nspname)||'.'||quote_ident(c.relname) || ';' + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + JOIN pg_attribute a ON a.attrelid = c.oid + WHERE c.relkind = 'r' + AND a.attnum > 0 + AND NOT a.attisdropped + AND pg_get_serial_sequence(quote_ident(n.nspname)||'.'||quote_ident(c.relname), a.attname) IS NOT NULL; + ") + + if [ -z "$SQL" ]; then + echo "No sequences found in $db" + continue + fi + + echo "Fixing sequences in $db..." + + # Execute generated statements + echo "$SQL" | psql -d "$db" + +done + +echo "== Done fixing sequences ==" + diff --git a/site-cookbooks/kosmos_postgresql/files/list_publications.sh b/site-cookbooks/kosmos_postgresql/files/list_publications.sh new file mode 100644 index 0000000..13ab742 --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/files/list_publications.sh @@ -0,0 +1,5 @@ +#!/bin/bash +for db in $(psql -Atqc "SELECT datname FROM pg_database WHERE datallowconn AND datname NOT IN ('template1','postgres')"); do + echo "DB: $db" + psql -d "$db" -Atqc "SELECT pubname FROM pg_publication;" +done diff --git a/site-cookbooks/kosmos_postgresql/files/list_replication_slots.sh b/site-cookbooks/kosmos_postgresql/files/list_replication_slots.sh new file mode 100644 index 0000000..591479b --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/files/list_replication_slots.sh @@ -0,0 +1,5 @@ +#!/bin/bash +psql -c " +SELECT slot_name, +pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) +FROM pg_replication_slots;" diff --git a/site-cookbooks/kosmos_postgresql/files/list_subscriptions.sh b/site-cookbooks/kosmos_postgresql/files/list_subscriptions.sh new file mode 100644 index 0000000..fbfbb2f --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/files/list_subscriptions.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -euo pipefail + +psql -Atqc " +SELECT datname +FROM pg_database +WHERE datallowconn + AND datname NOT IN ('template1','postgres') +" | while read -r db; do + result=$(psql -X -At -d "$db" -c "SELECT * FROM pg_stat_subscription;" 2>/dev/null || true) + + if [[ -n "$result" ]]; then + echo "==== DB: $db ====" + echo "$result" + fi +done diff --git a/site-cookbooks/kosmos_postgresql/files/restore_all_databases.sh b/site-cookbooks/kosmos_postgresql/files/restore_all_databases.sh new file mode 100644 index 0000000..18c4a7b --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/files/restore_all_databases.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -euo pipefail + +cd /tmp + +for f in dump_*.tar.zst; do + db=$(echo $f | sed "s/dump_\(.*\)\.tar\.zst/\1/") + echo "Restoring $db" + zstd -d "$f" -c | tar -xf - + pg_restore -j 4 -d "$db" dump_$db + rm -rf "dump_$db" +done diff --git a/site-cookbooks/kosmos_postgresql/files/restore_database.sh b/site-cookbooks/kosmos_postgresql/files/restore_database.sh new file mode 100644 index 0000000..49e7ce2 --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/files/restore_database.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -euo pipefail + +DB_NAME="${1:?Usage: $0 }" + +cd /tmp + +FILE="dump_${DB_NAME}.tar.zst" +DIR="dump_${DB_NAME}" + +echo "Restoring $DB_NAME" +zstd -d "$FILE" -c | tar -xf - +pg_restore -j 4 -d "$DB_NAME" "$DIR" +rm -rf "$DIR" diff --git a/site-cookbooks/kosmos_postgresql/libraries/helpers.rb b/site-cookbooks/kosmos_postgresql/libraries/helpers.rb index 18e245d..f87dda2 100644 --- a/site-cookbooks/kosmos_postgresql/libraries/helpers.rb +++ b/site-cookbooks/kosmos_postgresql/libraries/helpers.rb @@ -36,10 +36,8 @@ class Chef end end - def postgresql_service_name - postgresql_version = "12" - - "postgresql@#{postgresql_version}-main" + def postgresql_version + node['kosmos_postgresql']['postgresql_version'] end end end diff --git a/site-cookbooks/kosmos_postgresql/recipes/management_scripts.rb b/site-cookbooks/kosmos_postgresql/recipes/management_scripts.rb new file mode 100644 index 0000000..7a90bc2 --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/recipes/management_scripts.rb @@ -0,0 +1,121 @@ +# +# Cookbook:: kosmos_postgresql +# Recipe:: management_scripts +# + +credentials = data_bag_item('credentials', 'postgresql') + +cookbook_file "/usr/local/bin/pg_dump_all_databases" do + source "dump_all_databases.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_dump_database" do + source "dump_database.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_restore_all_databases" do + source "restore_all_databases.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_restore_database" do + source "restore_database.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_create_replication_publications" do + source "create_publications.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_create_replication_publication" do + source "create_publication.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_drop_replication_publications" do + source "drop_publications.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_list_replication_publications" do + source "list_publications.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_list_replication_slots" do + source "list_replication_slots.sh" + user "postgres" + group "postgres" + mode "0744" +end + +template "/usr/local/bin/pg_create_replication_subscriptions" do + source "create_subscriptions.sh.erb" + user "postgres" + group "postgres" + mode "0740" + variables pg_host: "pg.kosmos.local", + pg_port: 5432, + pg_user: "replication", + pg_pass: credentials["replication_password"] + sensitive true +end + +template "/usr/local/bin/pg_create_replication_subscription" do + source "create_subscription.sh.erb" + user "postgres" + group "postgres" + mode "0740" + variables pg_host: "pg.kosmos.local", + pg_port: 5432, + pg_user: "replication", + pg_pass: credentials["replication_password"] + sensitive true +end + +cookbook_file "/usr/local/bin/pg_drop_replication_subscriptions" do + source "drop_subscriptions.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_list_replication_subscriptions" do + source "list_subscriptions.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_fix_sequences_in_all_databases" do + source "fix_sequences.sh" + user "postgres" + group "postgres" + mode "0744" +end + +cookbook_file "/usr/local/bin/pg_fix_sequences" do + source "fix_sequences.sh" + user "postgres" + group "postgres" + mode "0744" +end diff --git a/site-cookbooks/kosmos_postgresql/recipes/primary.rb b/site-cookbooks/kosmos_postgresql/recipes/primary.rb index de7466f..73c0224 100644 --- a/site-cookbooks/kosmos_postgresql/recipes/primary.rb +++ b/site-cookbooks/kosmos_postgresql/recipes/primary.rb @@ -3,31 +3,6 @@ # Recipe:: primary # -postgresql_version = "12" -postgresql_service = "postgresql@#{postgresql_version}-main" - -service postgresql_service do - supports restart: true, status: true, reload: true -end - postgresql_custom_server postgresql_version do role "primary" end - -postgresql_access "zerotier members" do - access_type "host" - access_db "all" - access_user "all" - access_addr "10.1.1.0/24" - access_method "md5" - notifies :reload, "service[#{postgresql_service}]", :immediately -end - -postgresql_access "zerotier members replication" do - access_type "host" - access_db "replication" - access_user "replication" - access_addr "10.1.1.0/24" - access_method "md5" - notifies :reload, "service[#{postgresql_service}]", :immediately -end diff --git a/site-cookbooks/kosmos_postgresql/recipes/replica.rb b/site-cookbooks/kosmos_postgresql/recipes/replica.rb index b1dd345..7d355c4 100644 --- a/site-cookbooks/kosmos_postgresql/recipes/replica.rb +++ b/site-cookbooks/kosmos_postgresql/recipes/replica.rb @@ -3,54 +3,34 @@ # Recipe:: replica # -postgresql_version = "12" -postgresql_service = "postgresql@#{postgresql_version}-main" - postgresql_custom_server postgresql_version do role "replica" end -service postgresql_service do - supports restart: true, status: true, reload: true -end - postgresql_data_bag_item = data_bag_item('credentials', 'postgresql') primary = postgresql_primary -unless primary.nil? - # TODO - postgresql_data_dir = "/var/lib/postgresql/#{postgresql_version}/main" +if primary.nil? + Chef::Log.warn("No PostgreSQL primary node found. Skipping replication setup.") + return +end - # FIXME get zerotier IP - execute "set up replication" do - command <<-EOF -systemctl stop #{postgresql_service} +postgresql_service_name = "postgresql@#{postgresql_version}-main" +postgresql_data_dir = "/var/lib/postgresql/#{postgresql_version}/main" + +# TODO Replace pg.kosmos.local with private IP once available +# via proper node attribute +# https://gitea.kosmos.org/kosmos/chef/issues/263 +execute "set up replication" do + command <<-EOF +systemctl stop #{postgresql_service_name} mv #{postgresql_data_dir} #{postgresql_data_dir}.old pg_basebackup -h pg.kosmos.local -U replication -D #{postgresql_data_dir} -R chown -R postgres:postgres #{postgresql_data_dir} -systemctl start #{postgresql_service} - EOF - environment 'PGPASSWORD' => postgresql_data_bag_item['replication_password'] - sensitive true - not_if { ::File.exist? "#{postgresql_data_dir}/standby.signal" } - end - - postgresql_access "zerotier members" do - access_type "host" - access_db "all" - access_user "all" - access_addr "10.1.1.0/24" - access_method "md5" - notifies :reload, "service[#{postgresql_service}]", :immediately - end - - postgresql_access "zerotier members replication" do - access_type "host" - access_db "replication" - access_user "replication" - access_addr "10.1.1.0/24" - access_method "md5" - notifies :reload, "service[#{postgresql_service}]", :immediately - end +systemctl start #{postgresql_service_name} + EOF + environment 'PGPASSWORD' => postgresql_data_bag_item['replication_password'] + sensitive true + not_if { ::File.exist? "#{postgresql_data_dir}/standby.signal" } end diff --git a/site-cookbooks/kosmos_postgresql/recipes/replica_logical.rb b/site-cookbooks/kosmos_postgresql/recipes/replica_logical.rb new file mode 100644 index 0000000..7a3645b --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/recipes/replica_logical.rb @@ -0,0 +1,8 @@ +# +# Cookbook:: kosmos_postgresql +# Recipe:: replica_logical +# + +postgresql_custom_server postgresql_version do + role "replica_logical" +end diff --git a/site-cookbooks/kosmos_postgresql/resources/server.rb b/site-cookbooks/kosmos_postgresql/resources/server.rb index d5b38da..d0ae149 100644 --- a/site-cookbooks/kosmos_postgresql/resources/server.rb +++ b/site-cookbooks/kosmos_postgresql/resources/server.rb @@ -44,25 +44,28 @@ action :create do shared_buffers = if node['memory']['total'].to_i / 1024 < 1024 # < 1GB RAM "128MB" - else # >= 1GB RAM, use 50% of total RAM - "#{node['memory']['total'].to_i / 1024 / 2}MB" + else # >= 1GB RAM, use 25% of total RAM + "#{node['memory']['total'].to_i / 1024 / 4}MB" end additional_config = { max_connections: 200, # default shared_buffers: shared_buffers, + work_mem: "4MB", unix_socket_directories: "/var/run/postgresql", dynamic_shared_memory_type: "posix", timezone: "UTC", # default is GMT listen_addresses: "0.0.0.0", promote_trigger_file: "#{postgresql_data_dir}/failover.trigger", - wal_keep_segments: 256 + wal_level: "logical", + wal_keep_size: 4096, # 256 segments, 16MB each + max_replication_slots: 16 } postgresql_server_conf "main" do version postgresql_version additional_config additional_config - notifies :reload, "service[#{postgresql_service}]", :delayed + notifies :restart, "service[#{postgresql_service}]", :delayed end postgresql_user "replication" do @@ -70,6 +73,24 @@ action :create do replication true password postgresql_credentials['replication_password'] end + + postgresql_access "all members" do + access_type "host" + access_db "all" + access_user "all" + access_addr node['kosmos_postgresql']['access_addr'] + access_method "md5" + notifies :reload, "service[#{postgresql_service}]", :immediately + end + + postgresql_access "replication members" do + access_type "host" + access_db "replication" + access_user "replication" + access_addr node['kosmos_postgresql']['access_addr'] + access_method "md5" + notifies :reload, "service[#{postgresql_service}]", :immediately + end end action_class do diff --git a/site-cookbooks/kosmos_postgresql/templates/create_subscription.sh.erb b/site-cookbooks/kosmos_postgresql/templates/create_subscription.sh.erb new file mode 100644 index 0000000..0839775 --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/templates/create_subscription.sh.erb @@ -0,0 +1,31 @@ +#!/bin/bash +set -euo pipefail + +DB_NAME="${1:?Usage: $0 }" + +echo "== Processing DB: $DB_NAME ==" + +SLOT="migrate_slot_${DB_NAME}" +SUB="migrate_sub_${DB_NAME}" + +psql -d "$DB_NAME" -v ON_ERROR_STOP=1 < port=<%= @pg_port %> dbname=$DB_NAME user=<%= @pg_user %> password=<%= @pg_pass %>' + PUBLICATION migrate_pub + WITH ( + slot_name = '$SLOT', + create_slot = false, + copy_data = false, + enabled = true + ); + END IF; +END +\$\$; +SQL + +echo "== Done ==" diff --git a/site-cookbooks/kosmos_postgresql/templates/create_subscriptions.sh.erb b/site-cookbooks/kosmos_postgresql/templates/create_subscriptions.sh.erb new file mode 100644 index 0000000..9ee5078 --- /dev/null +++ b/site-cookbooks/kosmos_postgresql/templates/create_subscriptions.sh.erb @@ -0,0 +1,34 @@ +#!/bin/bash +set -e + +echo "== Creating subscriptions for all databases ==" + +for db in $(psql -Atqc "SELECT datname FROM pg_database WHERE datallowconn AND datname NOT IN ('template1','postgres')"); do + echo "Processing DB: $db" + + SLOT="migrate_slot_${db}" + SUB="migrate_sub_${db}" + + psql -d "$db" -v ON_ERROR_STOP=1 < port=<%= @pg_port %> dbname=$db user=<%= @pg_user %> password=<%= @pg_pass %>' + PUBLICATION migrate_pub + WITH ( + slot_name = '$SLOT', + create_slot = false, + copy_data = false, + enabled = true + ); + END IF; +END +\$\$; +SQL + +done + +echo "== Done =="