42 Commits

Author SHA1 Message Date
f0314e0b99 Update vendored cookbooks 2026-04-11 15:34:44 +04:00
ac4fb0c9ca Fix Java/Homebrew cookbook resource failing with recent Chef
Required by our Mastodon cookbook
2026-04-11 15:23:30 +04:00
d5e3d62522 Add new postgres cluster, remove old one 2026-04-11 15:22:34 +04:00
061880536b Fix akkounts systemd unit stop command
Typo (not using pumactl), but we don't need to specify it to do the
right thing anyway. systemd can just send sigterm on its own.
2026-04-11 14:56:15 +04:00
9de37cde96 Update doc 2026-04-11 14:55:32 +04:00
64d5d34d85 Update postgres roles
Reset to normal/final
2026-04-11 14:55:02 +04:00
db9177c9c6 Improve RAM usage allowance
Queries can spawn more processed and then use more RAM than
`shared_buffers`
2026-04-11 14:53:28 +04:00
c92f9157a5 Fix method undefined in heredoc 2026-04-11 14:52:53 +04:00
a89db454d0 Improve postgres management scripts 2026-04-11 14:51:51 +04:00
fddcd4899e Ignore default db for migration/management 2026-04-10 12:57:51 +04:00
8e11df4544 Update PG migration runbook 2026-04-10 12:49:28 +04:00
0020677ab2 Drone: Make database details configurable 2026-04-10 08:37:06 +04:00
09412f69e8 Move doc 2026-04-10 08:36:54 +04:00
bc3f291bd2 WIP Prepare postgres for migration by replication 2026-04-10 08:25:55 +04:00
6583cd7010 Upgrade WAL config for PG14 2026-04-08 15:41:13 +04:00
290af8177a Refactor postgres server recipes/resource 2026-04-08 15:41:10 +04:00
2cb5540a7b Add new postgres replica (v12) 2026-04-07 16:56:01 +04:00
002ad2ca62 Update Gandi API key
Co-authored-by: Greg Karékinian <greg@karekinian.com>
2026-04-07 16:53:43 +04:00
7710231fc4 Add CORS headers for Garage web access
Fixes Discourse plugin JS usage
2026-04-07 16:53:09 +04:00
Greg Karekinian
d68deb96e9 Update openresty submodule 2026-04-07 11:40:35 +02:00
01cdd000cb Update nodes 2026-03-27 14:30:46 +04:00
ea8e2de70a Merge pull request 'Use Ubuntu 22.04 for new VMs' (#521) from jammy_jellyfish into master
Reviewed-on: #521
2026-03-27 10:28:22 +00:00
8ad3674c4d Install libvirt CLI on KVM hosts 2026-03-27 14:27:28 +04:00
Râu Cao
25192ad3ce Use Ubuntu 22.04 for new VMs
Also, remove the custom config image generation and replace it with
`--cloud-init` options.
2026-03-26 20:35:30 +04:00
55b6e24f1e Merge pull request 'Configure Gitea commit signing with SSH key' (#623) from feature/237-gitea_ssh_signing into master
Reviewed-on: #623
Reviewed-by: Greg <greg@kosmos.org>
2026-03-19 13:27:55 +00:00
a23c7d536a Merge pull request 'Improve BTC price tracking script' (#624) from feature/btc_price_tracker into master
Reviewed-on: #624
2026-03-07 06:21:51 +00:00
d492cd18cc Improve BTC price tracking script
1. Robust API helper: Add make_request with retry logic for both
   GET (price data) and PUT (upload) requests
2. Arithmetic precision: Switch to awk for floating-point average
   calculation
3. Correct error handling: Updated get_price_data to return status
   codes and the main script to exit on failure
4. Safer JSON: Use jq to construct valid JSON payloads
5. Safety Flags: Add set -e/-o to fail fast on any command errors
2026-03-06 23:20:12 +04:00
161b78be97 Configure Gitea commit signing with SSH key 2026-02-13 17:29:23 +04:00
6e83384da5 Use more attributes for Gitea config 2026-02-13 16:07:24 +04:00
be8278fbdc Upgrade act_runner 2026-02-13 16:06:08 +04:00
ff3f05452f Merge pull request 'Update Gitea to 1.25.4' (#622) from chore/upgrade_gitea into master
Reviewed-on: #622
Reviewed-by: Râu Cao <raucao@kosmos.org>
2026-02-13 10:18:36 +00:00
Greg Karekinian
1fb66092fc Update Gitea to 1.25.4
Back to using the binary from upstream releases
2026-02-13 11:15:07 +01:00
Greg Karekinian
81691f7e21 Run systemctl daemon-reload on gitea service changes 2026-02-13 11:05:08 +01:00
e9dff82628 Merge pull request 'Add IPv6 support for all OpenResty sites' (#618) from feature/614-ipv6 into master
Reviewed-on: #618
2026-02-12 13:09:25 +00:00
0933e9caa0 Add IPv6 to all OpenResty sites
Co-authored-by: Greg Karékinian <greg@karekinian.com>
2026-02-12 17:05:14 +04:00
9f862a89cc Merge pull request 'Enable Gitea SSH via IPv6' (#613) from chore/612-enable_ipv6_ssh into master
Reviewed-on: #613
Reviewed-by: Greg <greg@kosmos.org>
2026-01-11 13:19:33 +00:00
039dbdf091 Enable Gitea SSH via IPv6
closes #612
2026-01-09 13:43:06 +07:00
e3559119be Update node info 2025-11-25 10:56:35 +00:00
16f95170ef Remove old node 2025-11-25 10:55:04 +00:00
36f5903271 Merge pull request 'Fix URL matcher for substr (vs strfry)' (#608) from bugfix/substr_url_matching into master
Reviewed-on: #608
2025-11-17 11:03:48 +00:00
fd9636441b Fix URL matcher for substr (vs strfry)
I wasn't able to reach https://nostr.kosmos.org/nodeinfo/2.1, which I
stumbled upon in an upstream PR. This one only matches exactly the paths
that substr is serving.

Tested/running in production.
2025-11-17 10:47:50 +01:00
aade479e5b Remove obsolete recipe 2025-11-12 13:47:00 +01:00
93 changed files with 1263 additions and 320 deletions

View File

@@ -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'

View File

@@ -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)

4
clients/garage-14.json Normal file
View File

@@ -0,0 +1,4 @@
{
"name": "garage-14",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAypINv1zTZ7+pyT0iRhik\n0W70ASYADo7qK7QyE9/3nu2sUrP1IjoNFsv/ceKwicH7Fw2Ei1o+yKZlKn7zJzY7\n93YRZndF04VH2bmqy0uOWK0Bdat7gCld5bvS6FmRflg7g64LFb33/64QIVsVGHGL\nYF2TO//x79t9JKcQDa4h5MOWzJNTFuEcUGa0gJjMYpWGVHEJSgRuIgyhXmyIJJgY\nguj6ymTm5+3VS7NzoNy2fbTt1LRpHb5UWrCR15oiLZiDSMLMx0CcGOCmrhvODi4k\n0umw+2NPd1G50s9z7KVbTqybuQ65se2amRnkVcNfaBIU5qk9bVqcmhZlEozmBZCd\ndwIDAQAB\n-----END PUBLIC KEY-----\n"
}

4
clients/garage-15.json Normal file
View File

@@ -0,0 +1,4 @@
{
"name": "garage-15",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy14sTt5gxVZi9C3KIEBu\nDyUgbb6jc3/GR22fNPTqV6uDHhxzhE2UsYwY/7yuA1RasdwHEOBWZaoC0Om5/Zmi\n8gn6//v1ILyLNaAcw+SQcxZkCN8Sk/0atRS9HYk1agE8Mvh72Fe2z3l+92VMefy7\nJwJUNNBTbnV2WVCchChoWnfhI7bkSLSHp0M2MO2pI+lkpSdmfkJSa5z9zihgxKO8\nXfvhryDCZNvfRVHhwc+ffpap0gLF0H9riGKE4FwLy4YqbuW1Tgm6bObb9bpOIw6Q\nVfH3kC/KMK5FlnxGmYtDkhRJ/wjGInRBk9WK/QOmjyd2FVxipEQmA4RdjlznRC9I\nrwIDAQAB\n-----END PUBLIC KEY-----\n"
}

4
clients/leo.json Normal file
View File

@@ -0,0 +1,4 @@
{
"name": "leo",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFfQsJnREjbXTtpT6BVt\naBaUzRmCQi8Du0TzeUG0ENrY0p5Exqleye2rC6bJlB3PER1xr5zdtuXLgbcVumIb\nzroU5JPtFbQk7r/pj0atT+UEYzl16iuEpprQ/bug+f0nE514USr6YG4G+tlZ/jBI\nSHsCQF1P8ufXFLW0ewC7rdvBkgA+DwK14naRxS4jO5MSl4wmNTjs/jymTg508mQq\nf5tG52t8qFdgn9pRdBXmyTpPtwK7I4rZ+1Qn+1E5m4oQUZsxh8Ba1bGbKotVO7Ua\nYL1yCGx7zRRUvLLIdSMvlRXTJBUSQtQ8P4QUDWTY1Na2w3t9sulKg2Lwsw8tktvC\nCwIDAQAB\n-----END PUBLIC KEY-----\n"
}

4
clients/postgres-10.json Normal file
View File

@@ -0,0 +1,4 @@
{
"name": "postgres-10",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2oBb5omC7ZionWhudgFm\n2NGcRXsI0c7+g1+0duaxj5dziaRTltqzpRJTfiJD6R36FcvEqwGc+qQgYSMzc1Xd\nY4OTvJFIDiFAmROm/DZYgFtTDldVNJZO2bbU3COYf/Z2Poq56gC4zLLd/zf6shgb\n2Mty8PlQ82JJAY9EMI3aAifdnZ1k/g4weFC4LFg9lUcNNXOwlAjp//LJ3ku3aY1r\nwW74msSeWEjE44YZdWyMYgM7Fy1hz5giHFQtRdOLemRCWQ8h26wn/cmWld7lsLg+\nlYqxokxWXGv8r5zR8kDTBkd0dxY7ZMbo7oESY4Uhuf4UReMe2ZGHto1E7w3llSj+\n7wIDAQAB\n-----END PUBLIC KEY-----\n"
}

4
clients/postgres-11.json Normal file
View File

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

4
clients/postgres-12.json Normal file
View File

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

View File

@@ -1,4 +0,0 @@
{
"name": "postgres-7",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArraIm6mXi0qgK4oWDs2I\nOIx+g/LPnfRd5aBXhoHcekGiJKttQTi5dRdN4+T6qVEC2h4Cc9qN47h2TZPLDh/M\neIZvu0AyicpectzXf6DtDZh0hFCnv47RDi9927op9tjMXk0SV1tLel7MN0dawATw\ny0vQkkr/5a3ZdiP4dFv+bdfVrj+Tuh85BYPVyX2mxq9F7Efxrt6rzVBiqr6uJLUY\nStpeB3CCalC4zQApKX2xrdtr2k8aJbqC6C//LiKbb7VKn+ZuZJ32L/+9HDEzQoFC\no0ZZPMhfnjcU+iSHYZuPMTJTNbwgRuOgpn9O8kZ239qYc59z7HEXwwWiYPDevbiM\nCQIDAQAB\n-----END PUBLIC KEY-----\n"
}

4
clients/postgres-9.json Normal file
View File

@@ -0,0 +1,4 @@
{
"name": "postgres-9",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dcE9HH0r5TBb/FGj2+e\nOw8ssoxeB61JmR4/psdZ6oPR08gxyqOY0ODziCmyIdXwFhjIcC44HjxCbcB8TU8G\nWGqlmfqWWIJW0x/2xOycHobAWDn5fC5ttTXkR3HC1TutX/2mH26mtfz9UjNdPaTo\nVZFMcxeaBCFSNlYC7hPUQ5f/qBdhhpLxP9uyzU+YFPqtwLP7g8EAUQObM4L+m6Q8\nqE7xgYpnhgaNrPsmvaVuoNylMGwyK0j1whOkcik8UgLprD70ISNSNxxcLehbvA3G\nPQPQRRuFF36fu2gECWGopbrFKwQGNfgJguQoXM1RQZQMQqWHPS933k5i6bi5pnhp\nzwIDAQAB\n-----END PUBLIC KEY-----\n"
}

View File

@@ -3,3 +3,5 @@ config:
line-length: false # MD013
no-duplicate-heading: false # MD024
reference-links-images: false # MD052
ignores:
- .github/copilot-instructions.md

View File

@@ -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*

View File

@@ -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)

View File

@@ -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": [

View File

@@ -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'

View File

@@ -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"]
},

View File

@@ -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

View File

@@ -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-]+)+$}

View File

@@ -1,23 +1,16 @@
{
"id": "gandi_api",
"key": {
"encrypted_data": "lU7/xYTmP5Sb6SsK5TNNIyegWozzBtUzpg7oDdl6gcz9FEMmG2ft0Ljh5Q==\n",
"iv": "EZPQD3C+wsP/mBhF\n",
"auth_tag": "vF9E8Pj4Z8quJJdOMg/QTw==\n",
"version": 3,
"cipher": "aes-256-gcm"
},
"access_token": {
"encrypted_data": "1Uw69JkNrmb8LU/qssuod1SlqxxrWR7TJQZeeivRrNzrMIVTEW/1uwJIYL6b\nM4GeeYl9lIRlMMmLBkc=\n",
"iv": "cc1GJKu6Cf4DkIgX\n",
"auth_tag": "ERem4S7ozG695kjvWIMghw==\n",
"encrypted_data": "+skwxHnpAj/3d3e2u7s7B9EydbETj8b0flWahvb5gt/o4JYFWHrhIyX/0IVa\n4wgmu08eDgU51i0knGA=\n",
"iv": "ONKrFCt8Oj3GKIQ5\n",
"auth_tag": "j9Hrk8ZZFMQub4NUO+2e4g==\n",
"version": 3,
"cipher": "aes-256-gcm"
},
"domains": {
"encrypted_data": "scZ5blsSjs54DlitR7KZ3enLbyceOR5q0wjHw1golQ==\n",
"iv": "oDcHm7shAzW97b4t\n",
"auth_tag": "62Zais9yf68SwmZRsmZ3hw==\n",
"encrypted_data": "lGfoPHdXEYYdJmoIA9M119wjVl1v4UzIv5gHADwx0A==\n",
"iv": "q6XKbxhW7X9ONxNt\n",
"auth_tag": "ns9WJH8Oe75siWu+sOZkRg==\n",
"version": 3,
"cipher": "aes-256-gcm"
}

287
doc/postgres/migration.md Normal file
View File

@@ -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
---

View File

@@ -9,7 +9,7 @@
"automatic": {
"fqdn": "akkounts-1",
"os": "linux",
"os_version": "5.4.0-216-generic",
"os_version": "5.4.0-223-generic",
"hostname": "akkounts-1",
"ipaddress": "192.168.122.160",
"roles": [

View File

@@ -12,6 +12,7 @@
},
"openresty": {
"listen_ip": "148.251.237.111",
"listen_ipv6": "2a01:4f8:202:804a::2",
"log_formats": {
"json": "{\"ip\":\"$remote_addr\",\"time\":\"$time_local\",\"host\":\"$host\",\"method\":\"$request_method\",\"uri\":\"$uri\",\"status\":$status,\"size\":$body_bytes_sent,\"referer\":\"$http_referer\",\"upstream_addr\":\"$upstream_addr\",\"upstream_response_time\":\"$upstream_response_time\",\"ua\":\"$http_user_agent\"}"
}
@@ -81,6 +82,7 @@
"timezone_iii::debian",
"ntp::default",
"ntp::apparmor",
"kosmos-base::journald_conf",
"kosmos-base::systemd_emails",
"apt::unattended-upgrades",
"kosmos-base::firewall",

View File

@@ -75,6 +75,7 @@
"timezone_iii::debian",
"ntp::default",
"ntp::apparmor",
"kosmos-base::journald_conf",
"kosmos-base::systemd_emails",
"apt::unattended-upgrades",
"kosmos-base::firewall",

65
nodes/garage-14.json Normal file
View File

@@ -0,0 +1,65 @@
{
"name": "garage-14",
"chef_environment": "production",
"normal": {
"knife_zero": {
"host": "10.1.1.151"
}
},
"automatic": {
"fqdn": "garage-14",
"os": "linux",
"os_version": "5.15.0-1095-kvm",
"hostname": "garage-14",
"ipaddress": "192.168.122.36",
"roles": [
"base",
"kvm_guest",
"garage_node"
],
"recipes": [
"kosmos-base",
"kosmos-base::default",
"kosmos_kvm::guest",
"kosmos_garage",
"kosmos_garage::default",
"kosmos_garage::firewall_rpc",
"kosmos_garage::firewall_apis",
"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",
"firewall::default"
],
"platform": "ubuntu",
"platform_version": "22.04",
"cloud": null,
"chef_packages": {
"chef": {
"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.2.13",
"ohai_root": "/opt/chef/embedded/lib/ruby/gems/3.1.0/gems/ohai-18.2.13/lib/ohai"
}
}
},
"run_list": [
"role[base]",
"role[kvm_guest]",
"role[garage_node]"
]
}

View File

@@ -1,34 +1,36 @@
{
"name": "postgres-8",
"name": "garage-15",
"chef_environment": "production",
"normal": {
"knife_zero": {
"host": "10.1.1.99"
"host": "10.1.1.82"
}
},
"automatic": {
"fqdn": "postgres-8",
"fqdn": "garage-15",
"os": "linux",
"os_version": "5.15.0-1059-kvm",
"hostname": "postgres-8",
"ipaddress": "192.168.122.100",
"os_version": "5.15.0-1095-kvm",
"hostname": "garage-15",
"ipaddress": "192.168.122.57",
"roles": [
"base",
"kvm_guest",
"postgresql_replica"
"garage_node"
],
"recipes": [
"kosmos-base",
"kosmos-base::default",
"kosmos_kvm::guest",
"kosmos_postgresql::hostsfile",
"kosmos_postgresql::replica",
"kosmos_postgresql::firewall",
"kosmos_garage",
"kosmos_garage::default",
"kosmos_garage::firewall_rpc",
"kosmos_garage::firewall_apis",
"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",
@@ -37,26 +39,27 @@
"postfix::_common",
"postfix::_attributes",
"postfix::sasl_auth",
"hostname::default"
"hostname::default",
"firewall::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",
"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"
}
}
},
"run_list": [
"role[base]",
"role[kvm_guest]",
"role[postgresql_replica]"
"role[garage_node]"
]
}

View File

@@ -50,13 +50,6 @@
"postfix::sasl_auth",
"hostname::default",
"firewall::default",
"kosmos_gitea::compile_from_source",
"git::default",
"git::package",
"kosmos-nodejs::default",
"nodejs::nodejs_from_package",
"nodejs::repo",
"golang::default",
"backup::default",
"logrotate::default"
],

56
nodes/leo.json Normal file
View File

@@ -0,0 +1,56 @@
{
"name": "leo",
"normal": {
"knife_zero": {
"host": "leo.kosmos.org"
}
},
"automatic": {
"fqdn": "leo",
"os": "linux",
"os_version": "5.15.0-164-generic",
"hostname": "leo",
"ipaddress": "5.9.81.116",
"roles": [
"base"
],
"recipes": [
"kosmos-base",
"kosmos-base::default",
"kosmos_kvm::host",
"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.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.2.13",
"ohai_root": "/opt/chef/embedded/lib/ruby/gems/3.1.0/gems/ohai-18.2.13/lib/ohai"
}
}
},
"run_list": [
"role[base]",
"recipe[kosmos_kvm::host]"
]
}

View File

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

View File

@@ -1,5 +1,5 @@
{
"name": "postgres-7",
"name": "postgres-12",
"chef_environment": "production",
"normal": {
"knife_zero": {
@@ -7,11 +7,11 @@
}
},
"automatic": {
"fqdn": "postgres-7",
"fqdn": "postgres-12",
"os": "linux",
"os_version": "5.4.0-1123-kvm",
"hostname": "postgres-7",
"ipaddress": "192.168.122.89",
"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",
@@ -41,17 +42,17 @@
"hostname::default"
],
"platform": "ubuntu",
"platform_version": "20.04",
"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",
"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"
}
}
},

View File

@@ -8,8 +8,8 @@ run_list %w(
override_attributes(
"gitea" => {
"repo" => "https://github.com/67P/gitea.git",
"revision" => "ldap_sync",
# "repo" => "https://github.com/67P/gitea.git",
# "revision" => "ldap_sync",
"log" => { "level" => "Info" }
},
)

View File

@@ -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"
]

View File

@@ -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"
]

View File

@@ -0,0 +1,8 @@
name "postgresql_replica_logical"
run_list [
"kosmos_postgresql::hostsfile",
"kosmos_postgresql::replica_logical",
"kosmos_postgresql::firewall",
"kosmos_postgresql::management_scripts"
]

View File

@@ -8,8 +8,8 @@ upstream _<%= @upstream_name %> {
<% if File.exist?(@ssl_cert) && File.exist?(@ssl_key) -%>
server {
server_name <%= @server_name %>;
listen 443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
ssl_certificate <%= @ssl_cert %>;
ssl_certificate_key <%= @ssl_key %>;

View File

@@ -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",

View File

@@ -11,7 +11,7 @@ proxy_cache_path <%= node[:openresty][:cache_dir] %>/akkounts levels=1:2
server {
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @domain %>;
if ($host != $server_name) {

View File

@@ -7,7 +7,7 @@ upstream _akkounts_api {
server {
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @domain %>;
ssl_certificate <%= @ssl_cert %>;

View File

@@ -1,52 +0,0 @@
#
# Cookbook Name:: kosmos-base
# Recipe:: andromeda_firewall
#
# The MIT License (MIT)
#
# Copyright:: 2019, Kosmos Developers
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# Temporary extra rules for Andromeda
firewall_rule 'bitcoind' do
port [8333, 8334, 8335]
protocol :tcp
command :allow
end
firewall_rule 'lnd' do
port [9736]
# port [9736, 8002]
protocol :tcp
command :allow
end
firewall_rule 'lightningd' do
port [9735]
protocol :tcp
command :allow
end
firewall_rule 'spark_wallet' do
port 8008
protocol :tcp
command :allow
end

View File

@@ -1,49 +1,86 @@
#!/bin/bash
set -e
set -o pipefail
# Calculate yesterday's date in YYYY-MM-DD format
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
echo "Starting price tracking for $YESTERDAY" >&2
# Helper function to perform HTTP requests with retries
# Usage: make_request <retries> <method> <url> [data] [header1] [header2] ...
make_request() {
local retries=$1
local method=$2
local url=$3
local data=$4
shift 4
local headers=("$@")
local count=0
local wait_time=3
local response
while [ "$count" -lt "$retries" ]; do
local curl_opts=(-s -S -f -X "$method")
if [ -n "$data" ]; then
curl_opts+=(-d "$data")
fi
for h in "${headers[@]}"; do
curl_opts+=(-H "$h")
done
if response=$(curl "${curl_opts[@]}" "$url"); then
echo "$response"
return 0
fi
echo "Request to $url failed (Attempt $((count+1))/$retries). Retrying in ${wait_time}s..." >&2
sleep "$wait_time"
count=$((count + 1))
done
echo "ERROR: Request to $url failed after $retries attempts" >&2
return 1
}
# Fetch and process rates for a fiat currency
get_price_data() {
local currency=$1
local data avg open24 last
data=$(curl -s "https://www.bitstamp.net/api/v2/ticker/btc${currency,,}/")
if [ $? -eq 0 ] && [ ! -z "$data" ]; then
if data=$(make_request 3 "GET" "https://www.bitstamp.net/api/v2/ticker/btc${currency,,}/" ""); then
echo "Successfully retrieved ${currency} price data" >&2
open24=$(echo "$data" | jq -r '.open_24')
last=$(echo "$data" | jq -r '.last')
avg=$(( (${open24%.*} + ${last%.*}) / 2 ))
avg=$(echo "$open24 $last" | awk '{printf "%.0f", ($1 + $2) / 2}')
echo $avg
else
echo "ERROR: Failed to retrieve ${currency} price data" >&2
exit 1
return 1
fi
}
# Get price data for each currency
usd_avg=$(get_price_data "USD")
eur_avg=$(get_price_data "EUR")
gbp_avg=$(get_price_data "GBP")
usd_avg=$(get_price_data "USD") || exit 1
eur_avg=$(get_price_data "EUR") || exit 1
gbp_avg=$(get_price_data "GBP") || exit 1
# Create JSON
json="{\"EUR\":$eur_avg,\"USD\":$usd_avg,\"GBP\":$gbp_avg}"
json=$(jq -n \
--argjson eur "$eur_avg" \
--argjson usd "$usd_avg" \
--argjson gbp "$gbp_avg" \
'{"EUR": $eur, "USD": $usd, "GBP": $gbp}')
echo "Rates: $json" >&2
# PUT in remote storage
response=$(curl -X PUT \
-H "Authorization: Bearer $RS_AUTH" \
-H "Content-Type: application/json" \
-d "$json" \
-w "%{http_code}" \
-s \
-o /dev/null \
"<%= @rs_base_url %>/$YESTERDAY")
if [ "$response" -eq 200 ] || [ "$response" -eq 201 ]; then
if make_request 3 "PUT" "<%= @rs_base_url %>/$YESTERDAY" "$json" \
"Authorization: Bearer $RS_AUTH" \
"Content-Type: application/json" > /dev/null; then
echo "Successfully uploaded price data" >&2
else
echo "ERROR: Failed to upload price data. HTTP status: $response" >&2
echo "ERROR: Failed to upload price data" >&2
exit 1
fi

View File

@@ -49,7 +49,7 @@ server {
client_max_body_size 100M;
server_name <%= @server_name %>;
listen 443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
access_log <%= node[:nginx][:log_dir] %>/btcpayserver.access.log json;
error_log <%= node[:nginx][:log_dir] %>/btcpayserver.error.log warn;

View File

@@ -7,7 +7,7 @@ upstream _lndhub {
server {
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @server_name %>;
add_header Strict-Transport-Security "max-age=15768000";

View File

@@ -49,7 +49,7 @@ server {
server_name <%= @server_name %>;
<% if File.exist?(@ssl_cert) && File.exist?(@ssl_key) -%>
listen 443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
<% else -%>
listen 80;
<% end -%>

View File

@@ -3,7 +3,7 @@
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @server_name %>;
ssl_certificate <%= @ssl_cert %>;

View File

@@ -7,7 +7,7 @@ upstream _express_<%= @server_name.gsub(".", "_") %> {
server {
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @server_name %>;
add_header Strict-Transport-Security "max-age=15768000";

View File

@@ -12,7 +12,7 @@ upstream _ipfs_api {
server {
server_name <%= @server_name %>;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
access_log /var/log/nginx/<%= @server_name %>.access.log;
error_log /var/log/nginx/<%= @server_name %>.error.log;

View File

@@ -21,7 +21,7 @@ proxy_cache_path /var/cache/nginx/mastodon levels=1:2
server {
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @server_name %>;
include <%= @shared_config_path %>;

View File

@@ -3,7 +3,7 @@
server {
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @domain %>;
root /var/www/<%= @domain %>/site;

View File

@@ -9,7 +9,7 @@ upstream _discourse {
server {
server_name <%= @server_name %>;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
ssl_certificate <%= @ssl_cert %>;
ssl_certificate_key <%= @ssl_key %>;

View File

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

View File

@@ -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

View File

@@ -8,7 +8,7 @@ upstream _drone {
server {
server_name <%= @server_name %>;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
ssl_certificate <%= @ssl_cert %>;
ssl_certificate_key <%= @ssl_key %>;

View File

@@ -4,7 +4,7 @@ upstream garage_s3 {
server {
listen <%= "#{node[:openresty][:listen_ip]}:" if node[:openresty][:listen_ip] %>443 ssl http2;
listen [::]:443 http2 ssl;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
ssl_certificate <%= @ssl_cert %>;
ssl_certificate_key <%= @ssl_key %>;

View File

@@ -1,6 +1,6 @@
server {
listen <%= "#{node[:openresty][:listen_ip]}:" if node[:openresty][:listen_ip] %>443 ssl http2;
listen [::]:443 http2 ssl;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @server_name %>;
@@ -18,6 +18,7 @@ server {
}
location / {
add_header 'Access-Control-Allow-Origin' '*' always;
proxy_intercept_errors on;
proxy_cache garage_cache;
proxy_pass http://garage_web;

View File

@@ -1,11 +1,12 @@
node.default["gitea"]["version"] = "1.23.8"
node.default["gitea"]["checksum"] = "827037e7ca940866918abc62a7488736923396c467fcb4acd0dd9829bb6a6f4c"
node.default["gitea"]["version"] = "1.25.4"
node.default["gitea"]["checksum"] = "a3031853e67c53714728ef705642c9046a11fb0ea356aff592e23efe6114607d"
node.default["gitea"]["repo"] = nil
node.default["gitea"]["revision"] = nil
node.default["gitea"]["working_directory"] = "/var/lib/gitea"
node.default["gitea"]["port"] = 3000
node.default["gitea"]["postgresql_host"] = "localhost:5432"
node.default["gitea"]["domain"] = "gitea.kosmos.org"
node.default["gitea"]["email"] = "gitea@kosmos.org"
node.default["gitea"]["config"] = {
"log": {
@@ -22,5 +23,5 @@ node.default["gitea"]["config"] = {
}
}
node.default["gitea"]["act_runner"]["version"] = "0.2.6"
node.default["gitea"]["act_runner"]["checksum"] = "234c2bdb871e7b0bfb84697f353395bfc7819faf9f0c0443845868b64a041057"
node.default["gitea"]["act_runner"]["version"] = "0.2.13"
node.default["gitea"]["act_runner"]["checksum"] = "3acac8b506ac8cadc88a55155b5d6378f0fab0b8f62d1e0c0450f4ccd69733e2"

View File

@@ -19,6 +19,17 @@ jwt_secret = gitea_data_bag_item["jwt_secret"]
internal_token = gitea_data_bag_item["internal_token"]
secret_key = gitea_data_bag_item["secret_key"]
apt_repository "git-core-ppa" do
uri "http://ppa.launchpad.net/git-core/ppa/ubuntu"
components ["main"]
key "E1DF1F24"
action :add
only_if do
node['platform'] == 'ubuntu' &&
Gem::Version.new(node['platform_version']) < Gem::Version.new('22.04')
end
end
package "git"
user "git" do
@@ -26,6 +37,13 @@ user "git" do
home "/home/git"
end
directory "/home/git/.ssh" do
owner "git"
group "git"
mode "0700"
recursive true
end
directory working_directory do
owner "git"
group "git"
@@ -78,6 +96,8 @@ if node.chef_environment == "production"
end
config_variables = {
domain: node["gitea"]["domain"],
email: node["gitea"]["email"],
working_directory: working_directory,
git_home_directory: git_home_directory,
repository_root_directory: repository_root_directory,
@@ -98,6 +118,16 @@ config_variables = {
s3_bucket: gitea_data_bag_item["s3_bucket"]
}
bash "Generate git ed25519 keypair" do
user "git"
group "git"
cwd git_home_directory
code <<-EOH
ssh-keygen -t ed25519 -f #{git_home_directory}/.ssh/id_ed25519
EOH
creates "#{git_home_directory}/.ssh/id_ed25519"
end
template "#{config_directory}/app.ini" do
source "app.ini.erb"
owner "git"
@@ -129,7 +159,7 @@ template "/etc/systemd/system/gitea.service" do
git_home_directory: git_home_directory,
config_directory: config_directory,
gitea_binary_path: gitea_binary_path
notifies :run, "execute[systemctl daemon-reload]", :delayed
notifies :run, "execute[systemctl daemon-reload]", :immediately
end
service "gitea" do

View File

@@ -2,12 +2,12 @@ APP_NAME = Gitea
RUN_MODE = prod
[server]
SSH_DOMAIN = gitea.kosmos.org
SSH_DOMAIN = <%= @domain %>
HTTP_PORT = 3000
DISABLE_SSH = false
SSH_PORT = 22
PROTOCOL = http
DOMAIN = gitea.kosmos.org
DOMAIN = <%= @domain %>
# Gitea is running behind an nginx reverse load balancer, use an HTTPS root URL
ROOT_URL = https://%(DOMAIN)s
# REDIRECT_OTHER_PORT = true
@@ -30,6 +30,16 @@ MAX_OPEN_CONNS = 20
ROOT = <%= @repository_root_directory %>
DISABLE_DOWNLOAD_SOURCE_ARCHIVES = true
[repository.signing]
SIGNING_KEY = <%= @git_home_directory %>/.ssh/id_ed25519.pub
SIGNING_NAME = Gitea
SIGNING_EMAIL = git@<%= @domain %>
SIGNING_FORMAT = ssh
INITIAL_COMMIT = always
CRUD_ACTIONS = always
WIKI = always
MERGES = always
# [indexer]
# ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
@@ -46,7 +56,7 @@ SMTP_ADDR = <%= @smtp_addr %>
SMTP_PORT = <%= @smtp_port %>
USER = <%= @smtp_user %>
PASSWD = <%= @smtp_password %>
FROM = gitea@kosmos.org
FROM = <%= @email %>
[security]
INTERNAL_TOKEN = <%= @internal_token %>

View File

@@ -4,5 +4,6 @@ upstream _gitea_ssh {
server {
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>22;
listen [::]:22;
proxy_pass _gitea_ssh;
}

View File

@@ -6,7 +6,7 @@ upstream _gitea_web {
server {
server_name <%= @server_name %>;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
ssl_certificate <%= @ssl_cert %>;
ssl_certificate_key <%= @ssl_key %>;

View File

@@ -1,9 +1,9 @@
release = "20240514"
release = "20260320"
img_filename = "ubuntu-22.04-server-cloudimg-amd64-disk-kvm"
node.default["kosmos_kvm"]["host"]["qemu_base_image"] = {
"url" => "https://cloud-images.ubuntu.com/releases/jammy/release-#{release}/#{img_filename}.img",
"checksum" => "2e7698b3ebd7caead06b08bd3ece241e6ce294a6db01f92ea12bcb56d6972c3f",
"checksum" => "f7173eb7137b4f0ebeaea8fffe68ecdab1e3c787bde1fd8dfdf27103554332b3",
"path" => "/var/lib/libvirt/images/base/#{img_filename}-#{release}.qcow2"
}

View File

@@ -3,7 +3,7 @@
# Recipe:: host
#
package %w(virtinst libvirt-daemon-system)
package %w(virtinst libvirt-daemon-system libvirt-clients)
directory "/var/lib/libvirt/images/base" do
recursive true

View File

@@ -17,7 +17,7 @@ DISKSIZE=${4:-10} # 10GB default
# Directory where image files will be stored
IMAGE_DIR=/var/lib/libvirt/images
IMAGE_PATH=$IMAGE_DIR/${VMNAME}.qcow2
CIDATA_PATH=${IMAGE_DIR}/cidata-${VMNAME}.iso
CIDATA_PATH=${IMAGE_DIR}/${VMNAME}-cloudinit
BASE_FILE=<%= @base_image_path %>
# Create the VM image if it does not already exist
@@ -38,9 +38,8 @@ qemu-img info "$IMAGE_PATH"
# Check if the cloud-init metadata file exists
# if not, generate it
if [ ! -r $CIDATA_PATH ]; then
pushd $(dirname $CIDATA_PATH)
mkdir -p $VMNAME
cd $VMNAME
mkdir -p $CIDATA_PATH
pushd $CIDATA_PATH
cat > user-data <<-EOS
#cloud-config
@@ -62,25 +61,19 @@ instance-id: $VMNAME
local-hostname: $VMNAME
EOS
genisoimage -output "$CIDATA_PATH" -volid cidata -joliet -rock user-data meta-data
chown libvirt-qemu:kvm "$CIDATA_PATH"
chmod 600 "$CIDATA_PATH"
popd
fi
# setting --os-variant to ubuntu20.04 and ubuntu18.04 breaks SSH and networking
virt-install \
--name "$VMNAME" \
--ram "$RAM" \
--vcpus "$CPUS" \
--cpu host \
--arch x86_64 \
--os-type linux \
--os-variant ubuntu16.04 \
--osinfo detect=on,name=ubuntujammy \
--hvm \
--virt-type kvm \
--disk "$IMAGE_PATH" \
--cdrom "$CIDATA_PATH" \
--boot hd \
--network=bridge=virbr0,model=virtio \
--graphics none \
@@ -88,4 +81,5 @@ virt-install \
--console pty \
--channel unix,mode=bind,path=/var/lib/libvirt/qemu/$VMNAME.guest_agent.0,target_type=virtio,name=org.qemu.guest_agent.0 \
--autostart \
--import
--import \
--cloud-init root-password-generate=off,disable=on,meta-data=$CIDATA_PATH/meta-data,user-data=$CIDATA_PATH/user-data

View File

@@ -12,7 +12,7 @@ upstream _<%= @app_name %> {
server {
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @server_name %>;
access_log <%= node[:nginx][:log_dir] %>/<%= @app_name %>.access.log; # TODO json_liquor_cabinet;

View File

@@ -0,0 +1 @@
node.default["openresty"]["listen_ipv6"] = "::"

View File

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

View File

@@ -0,0 +1,31 @@
#!/bin/bash
set -euo pipefail
DB_NAME="${1:?Usage: $0 <database_name>}"
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 =="

View File

@@ -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 <<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}"
if ! psql -d "$db" -Atqc "SELECT 1 FROM pg_replication_slots WHERE slot_name = '$SLOT'" | grep -q 1; then
echo " Creating slot: $SLOT"
psql -d "$db" -c "SELECT pg_create_logical_replication_slot('$SLOT', 'pgoutput');"
else
echo " Slot already exists: $SLOT"
fi
done
echo "== Done =="

View File

@@ -0,0 +1,34 @@
#!/bin/bash
set -e
echo "== Dropping subscriptions slots and publications =="
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}"
# Drop slot if exists
if psql -d "$db" -Atqc "SELECT 1 FROM pg_replication_slots WHERE slot_name = '$SLOT'" | grep -q 1; then
echo " Dropping slot: $SLOT"
psql -d "$db" -c "SELECT pg_drop_replication_slot('$SLOT');"
else
echo " Slot not found: $SLOT"
fi
# Drop publication if exists
psql -d "$db" -v ON_ERROR_STOP=1 <<SQL
DO \$\$
BEGIN
IF EXISTS (
SELECT 1 FROM pg_publication WHERE pubname = 'migrate_pub'
) THEN
DROP PUBLICATION migrate_pub;
END IF;
END
\$\$;
SQL
done
echo "== Done =="

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -e
echo "== Dropping subscriptions =="
for db in $(psql -Atqc "SELECT datname FROM pg_database WHERE datallowconn AND datname NOT IN ('template1','postgres')"); do
echo "Processing DB: $db"
SUB="migrate_sub_${db}"
# Check if subscription exists
EXISTS=$(psql -d "$db" -Atqc "SELECT 1 FROM pg_subscription WHERE subname = '$SUB'")
if [ "$EXISTS" = "1" ]; then
echo " Found subscription: $SUB"
# Disable first (good practice)
psql -d "$db" -c "ALTER SUBSCRIPTION $SUB DISABLE;"
# Drop it (must be top-level)
psql -d "$db" -c "DROP SUBSCRIPTION $SUB;"
else
echo " No subscription: $SUB"
fi
done
echo "== Done =="

View File

@@ -0,0 +1,9 @@
#!/bin/bash
cd /tmp && \
(pg_dumpall --globals-only > 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_{}
"

View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail
DB_NAME="${1:?Usage: $0 <database_name>}"
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}"

View File

@@ -0,0 +1,35 @@
#!/bin/bash
set -e
DB="$1"
if [ -z "$DB" ]; then
echo "Usage: $0 <database>"
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 =="

View File

@@ -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 =="

View File

@@ -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

View File

@@ -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;"

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,14 @@
#!/bin/bash
set -euo pipefail
DB_NAME="${1:?Usage: $0 <database_name>}"
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"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,15 @@
#
# Cookbook:: kosmos_postgresql
# Recipe:: replica_logical
#
postgresql_custom_server postgresql_version do
role "replica_logical"
end
# primary = postgresql_primary
#
# if primary.nil?
# Chef::Log.warn("No PostgreSQL primary node found. Skipping replication setup.")
# return
# end

View File

@@ -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

View File

@@ -0,0 +1,31 @@
#!/bin/bash
set -euo pipefail
DB_NAME="${1:?Usage: $0 <database_name>}"
echo "== Processing DB: $DB_NAME =="
SLOT="migrate_slot_${DB_NAME}"
SUB="migrate_sub_${DB_NAME}"
psql -d "$DB_NAME" -v ON_ERROR_STOP=1 <<SQL
DO \$\$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_subscription WHERE subname = '$SUB'
) THEN
CREATE SUBSCRIPTION $SUB
CONNECTION 'host=<%= @pg_host %> 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 =="

View File

@@ -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 <<SQL
DO \$\$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_subscription WHERE subname = '$SUB'
) THEN
CREATE SUBSCRIPTION $SUB
CONNECTION 'host=<%= @pg_host %> 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 =="

View File

@@ -6,7 +6,7 @@ upstream _<%= @upstream_name %> {
server {
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @domain %>;

View File

@@ -13,7 +13,7 @@ upstream _substr {
server {
server_name <%= @domain %>;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
access_log "/var/log/nginx/<%= @domain %>.access.log";
error_log "/var/log/nginx/<%= @domain %>.error.log";
@@ -25,7 +25,7 @@ server {
alias /var/www/assets.kosmos.org/site/img/favicon.ico;
}
location ~* ^/[@~n]|^/assets {
location ~ ^/(?:@|~|npub|naddr|nprofile|assets/) {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://_substr;

View File

@@ -3,7 +3,7 @@
server {
server_name <%= @domain %>;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
access_log <%= node[:openresty][:log_dir] %>/<%= @domain %>.access.log;
error_log <%= node[:openresty][:log_dir] %>/<%= @domain %>.error.log warn;

View File

@@ -3,7 +3,7 @@
server {
server_name <%= @domain %>;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
root /var/www/<%= @domain %>/public;

View File

@@ -3,6 +3,7 @@
server {
server_name _;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>80 default_server;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:80 default_server;
location / {
return 301 https://<%= @domain %>;
@@ -12,7 +13,7 @@ server {
server {
server_name <%= @domain %>;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2 default_server;
if ($host != $server_name) {
return 307 $scheme://$server_name;

View File

@@ -5,8 +5,8 @@ upstream _<%= @app_name %> {
<% if File.exist?(@ssl_cert) && File.exist?(@ssl_key) -%>
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
server_name <%= @server_name %>;
access_log <%= node[:nginx][:log_dir] %>/<%= @app_name %>.access.log json;

View File

@@ -8,7 +8,7 @@ upstream _rs_discourse {
server {
server_name <%= @server_name %>;
listen <%= "#{node['openresty']['listen_ip']}:" if node['openresty']['listen_ip'] %>443 ssl http2;
listen [::]:443 ssl http2;
listen <%= "[#{node['openresty']['listen_ipv6']}]" %>:443 ssl http2;
ssl_certificate <%= @ssl_cert %>;
ssl_certificate_key <%= @ssl_key %>;