Skip to content

feat: debian support#381

Open
jason-lynch wants to merge 11 commits into
feat/PLAT-512/update-database-version-iifrom
feat/PLAT-579/debian-support-ii
Open

feat: debian support#381
jason-lynch wants to merge 11 commits into
feat/PLAT-512/update-database-version-iifrom
feat/PLAT-579/debian-support-ii

Conversation

@jason-lynch
Copy link
Copy Markdown
Member

@jason-lynch jason-lynch commented May 9, 2026

Summary

This PR adds support for Debian-based Linux distributions, such as Debian and Ubuntu. After this PR, we will start publishing a .deb file in our releases that users can download and install, as they can with the .rpm file we currently publish.

Changes

This PR is split into granular commits. I highly recommend reviewing the commits separately rather than looking at the entire diff at once. Some of the more notable commits are:

  • feat: debian support
    • Adds Debian support to the systemd orchestrator implementation.
  • docs: add deb instructions to systemd doc
    • Updates the systemd installation guide to include instructions for the deb packages.

Testing

# ensure your dev and dev-lima environments are stopped
make dev-reset
make dev-lima-reset

# make a test release to build the packages
make goreleaser-test-release

# deploy three VMs with debian 13
# this can take a while.
limactl start -y --name=deb-test-1 lima/debian-13-template.yaml
limactl start -y --name=deb-test-2 lima/debian-13-template.yaml
limactl start -y --name=deb-test-3 lima/debian-13-template.yaml

# these machines will have your home directory mounted, so there's
# no need to copy the deb package to them.

# SSH to each machine and follow the installation guide in the
# systemd.md file, e.g.
ssh -F ~/.lima/deb-test-1/ssh.config lima-deb-test-1 

# follow the installation guide in the systemd.md file on each machine
# except that instead of downloading the deb file, install the one from
# your mounted home directory, e.g.
sudo apt install /Users/jasonlynch/workspace/pgEdge/control-plane/dist/pgedge-control-plane_0.8.0-SNAPSHOT-bdafd43f_linux_arm64.deb

Notes for Reviewers

PLAT-579

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1c8a92ec-748c-47ca-8d33-a15cd8a8d0ed

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds multi-OS support (Rocky, Ubuntu, Debian) to the pgEdge Control Plane, removes the gosigar dependency, and makes database ownership dynamically resolved. It includes Lima templates for different operating systems, Ansible provisioning roles with OS-specific package management, new Apt package manager support alongside DNF, and comprehensive systemd service documentation for both RPM and Deb installations.

Changes

Multi-OS Support with Dynamic Database Ownership

Layer / File(s) Summary
Configuration & Data Contracts
server/internal/config/config.go, server/internal/pgbackrest/config.go, docs/installation/configuration.md
New DatabaseOwnerGID config field and pgbackrest.ConfigOptions.InstanceID for lock-path isolation; documentation updates for both fields.
System Resource Probing
server/internal/orchestrator/systemd/memory.go, server/internal/orchestrator/systemd/memory_test.go, server/internal/orchestrator/systemd/provide.go
Replace gosigar with direct /proc/meminfo reading; add OS family detection via /etc/os-release to select Dnf vs Apt package manager.
Package Manager Abstraction
server/internal/orchestrator/systemd/packages.go, server/internal/orchestrator/systemd/packages_test.go, server/internal/orchestrator/systemd/apt.go, server/internal/orchestrator/systemd/apt_test.go, server/internal/orchestrator/systemd/dnf.go, server/internal/orchestrator/systemd/dnf_test.go
New ExecCommand interface, processPackageList helper, and Apt implementation; refactor Dnf to use rpm query with injected exec function for testability.
Orchestrator Core & Owner Resolution
server/internal/orchestrator/systemd/orchestrator.go, server/internal/orchestrator/systemd/patroni_unit.go
Systemd orchestrator replaces gosigar with readTotalMemory(); new databaseOwnerIDs() helper performs dynamic uid/gid lookup with config overrides; pass resolved owner to PatroniUnitOptions.
Swarm Orchestrator & pgBackRest
server/internal/orchestrator/swarm/orchestrator.go, server/internal/orchestrator/swarm/pgbackrest_config.go, server/internal/orchestrator/common/pgbackrest_config.go
Add default database owner constants; propagate resolved UID/GID across resource ownership; include InstanceID in pgBackRest lock-path configuration.
Golden Tests & Unit Tests
server/internal/orchestrator/systemd/golden_test/.../PatroniUnitOptions/*.service, server/internal/orchestrator/systemd/unit_options_test.go, server/internal/pgbackrest/config_test.go, e2e/whole_cluster_test.go
Update golden service files to use User=26; unit options test passes databaseOwner parameter; pgBackRest tests set InstanceID and verify lock-path; e2e replication validation uses role-aware instance filtering.
Lima Multi-OS Provisioning
lima/vars.yaml, lima/Makefile, lima/deploy.yaml, lima/roles/deb_prerequisites/tasks/main.yaml, lima/roles/deb_prerequisites/templates/pgedge-old.sources.tmpl, lima/roles/rhel_prerequisites/vars/main.yaml, lima/roles/start_vms/tasks/main.yaml, lima/debian-13-template.yaml, lima/ubuntu-24.04-template.yaml, lima/ansible.cfg
Add Rocky/Ubuntu/Debian Lima templates; new DEV_LIMA_OS variable for OS selection; Ansible conditionals for OS-family-specific prerequisite roles (rhel vs deb); deb role installs pgEdge packages, etcd, Go, chrony, delve; rhel role provides version/URL variables.
Packaging & Release
.goreleaser.yaml, packaging/deb/postinstall.sh, packaging/rpm/postinstall.sh, packaging/preremove.sh, go.mod, NOTICE.txt, Makefile
Goreleaser adds deb format with package-specific postinstall overrides; new scripts handle systemd daemon reload and service management on upgrade/removal; remove gosigar dependency and NOTICE entry; add DEV_LIMA_OS to top-level Makefile.
Documentation
docs/installation/systemd.md, docs/development/running-locally.md
Comprehensive systemd guide with separate RPM and Deb installation workflows, configuration sections clarified; Lima environment docs describe default Rocky 9 and DEV_LIMA_OS override for Ubuntu/Debian.

Poem

🐰 Hops with glee through rocky terrain,
Dances through ubuntu and debian domain,
No more gosigar, just /proc to explore,
Dynamic ownerships the hare does adore,
Multi-OS harmony, now in the core!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: debian support' accurately and clearly summarizes the main change - adding Debian-based Linux distribution support to the project.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description includes all required template sections with substantive content: summary clearly states Debian support purpose, changes are listed with notable commits highlighted, comprehensive testing instructions are provided, and the checklist is partially addressed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/PLAT-579/debian-support-ii

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jason-lynch
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 9, 2026

Up to standards ✅

🟢 Issues 1 medium

Results:
1 new issue

Category Results
Complexity 1 medium

View in Codacy

🟢 Metrics 53 complexity · 2 duplication

Metric Results
Complexity 53
Duplication 2

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (2)
packaging/rpm/postinstall.sh (1)

3-3: 💤 Low value

Consider quoting $1 for robustness.

While $1 is always numeric in RPM scriptlets, quoting it would satisfy ShellCheck and improve defensive coding.

🔧 Suggested fix
-if [ $1 -ge 1 ]; then
+if [ "$1" -ge 1 ]; then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packaging/rpm/postinstall.sh` at line 3, The if test uses an unquoted
positional parameter: change the conditional in the postinstall script from
using $1 unquoted to a quoted form (e.g., "$1") so the shell handles empty or
special values safely and satisfies ShellCheck; update the if line in
packaging/rpm/postinstall.sh where the snippet reads if [ $1 -ge 1 ]; then to
use the quoted positional parameter.
lima/roles/deb_prerequisites/tasks/main.yaml (1)

92-95: ⚡ Quick win

Pin Delve to a specific version instead of @latest.

Go is pinned to 1.25.5 in this role, but Delve uses @latest, creating non-reproducible provisioning. When Delve releases a new version, provisioning may fail or behave unexpectedly if the new release doesn't align with the Go 1.25.x toolchain. Pin Delve to v1.25.2 (the latest Delve release supporting Go 1.25):

Suggested fix
- name: Install delve debugger
  ansible.builtin.command: /usr/local/go/bin/go install github.com/go-delve/delve/cmd/dlv@latest
+ ansible.builtin.command: /usr/local/go/bin/go install github.com/go-delve/delve/cmd/dlv@v1.25.2
  args:
    creates: /root/go/bin/dlv
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lima/roles/deb_prerequisites/tasks/main.yaml` around lines 92 - 95, The task
"Install delve debugger" currently uses ansible.builtin.command to run
"/usr/local/go/bin/go install github.com/go-delve/delve/cmd/dlv@latest", which
makes provisioning non-reproducible; update that command to pin Delve to the
v1.25.2 tag (e.g. replace "@latest" with "@v1.25.2") so the task named "Install
delve debugger" and the creates check "/root/go/bin/dlv" always install a known
compatible Delve build for Go 1.25.x.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/installation/systemd.md`:
- Line 157: The doc uses "sudo apt install
pgedge-control-plane_${VERSION#v}_linux_${ARCH}.deb" (and the same pattern at
the other occurrence) which can be misinterpreted by apt; change both
occurrences to pass a local-file path (e.g., prefix with ./) so apt installs the
downloaded .deb file reliably by path rather than attempting package-name
resolution.
- Around line 51-52: Update the sentence that currently mentions installing the
`pgedge-control-plane` RPM to use inclusive package-format wording; change the
reference to something like "after installing the `pgedge-control-plane`
package" or "after installing the `pgedge-control-plane` package (RPM or DEB)"
so Debian/Ubuntu users are not confused—locate the occurrence of the string
`pgedge-control-plane` in docs/installation/systemd.md and replace the
RPM-specific phrasing accordingly.
- Around line 319-320: The package name in the upgrade command is inconsistent:
replace the literal "pgedge-postgresql18" with the Debian-style package name
"pgedge-postgresql-18" to match the earlier documented pattern
"pgedge-postgresql-<major>" so the apt upgrade command will succeed; update the
text containing "pgedge-postgresql18" accordingly.

In `@e2e/whole_cluster_test.go`:
- Around line 127-133: The loop currently treats any error from row.Scan as
transient; change it to inspect the error returned by row.Scan: if it's
pgx.ErrNoRows or a pgconn.PgError with Code == "42P01" (table not found) then
treat as transient (sleep and continue), otherwise immediately fail the test
(use t.Fatalf or return the error) so real SQL/connection errors aren't masked;
locate the check around conn.QueryRow(...).Scan(&actual) and replace the blanket
continue with explicit error-type checks for pgx.ErrNoRows and pg error code
"42P01".

In `@lima/roles/deb_prerequisites/tasks/main.yaml`:
- Around line 86-91: The task named "Restart chronyd" uses
ansible.builtin.systemd_service with name set to "chronyd" which is incorrect on
Debian/Ubuntu; change the service name to "chrony" (i.e., update the
ansible.builtin.systemd_service task in the "Restart chronyd" task so name:
chrony, keeping state: restarted, enabled: true and the when: chronycfg.changed
condition intact) so the unit matches Debian/Ubuntu's chrony.service.

In `@lima/roles/deb_prerequisites/templates/pgedge-old.sources.tmpl`:
- Around line 1-5: The apt source template pgedge-old.sources.tmpl currently
contains the unsafe token "Trusted: yes"; remove that line and instead add a
"Signed-By" entry that references the repository key installed by the pgEdge
release package (e.g., Signed-By: pgedge-archive-keyring.gpg), ensuring the repo
uses apt-secure signature verification; update the template so the block reads
the same (Types/Suites/Components/URIs) but replaces "Trusted: yes" with the
Signed-By directive and confirm the release package provides the named keyring.

In `@server/internal/orchestrator/systemd/memory_test.go`:
- Line 14: The require.Equal call in memory_test.go currently passes the actual
value first and the expected value second; update the call in the test (the
require.Equal invocation) to use the correct Testify argument order by swapping
the second and third parameters so that expected is passed before actual (i.e.,
require.Equal(t, expected, out)).

In `@server/internal/orchestrator/systemd/patroni_unit.go`:
- Around line 17-18: PatroniUnitOptions currently only accepts the database
owner's UID; update its signature to also accept the numeric GID (e.g.,
databaseOwnerGID / ownerGID) and propagate that value into the generated systemd
options by adding a "Group" unit option alongside the existing "User" option (in
the code that constructs the []*unit.UnitOption returned by PatroniUnitOptions).
Ensure the new parameter type matches the existing owner UID type, update all
callers to pass the computed OwnerGID, and set the systemd directive to the
numeric GID so the unit runs with the configured group.

In `@server/internal/orchestrator/systemd/provide.go`:
- Around line 79-99: The getOSFamily function currently scans /etc/os-release
but doesn't check for scanning errors; after the for scanner.Scan() loop add a
check of scanner.Err() and if non-nil return OSFamilyUnknown (or empty) with
that error so I/O errors aren't silently ignored; update references in
getOSFamily to propagate the scanner error instead of always returning nil.

---

Nitpick comments:
In `@lima/roles/deb_prerequisites/tasks/main.yaml`:
- Around line 92-95: The task "Install delve debugger" currently uses
ansible.builtin.command to run "/usr/local/go/bin/go install
github.com/go-delve/delve/cmd/dlv@latest", which makes provisioning
non-reproducible; update that command to pin Delve to the v1.25.2 tag (e.g.
replace "@latest" with "@v1.25.2") so the task named "Install delve debugger"
and the creates check "/root/go/bin/dlv" always install a known compatible Delve
build for Go 1.25.x.

In `@packaging/rpm/postinstall.sh`:
- Line 3: The if test uses an unquoted positional parameter: change the
conditional in the postinstall script from using $1 unquoted to a quoted form
(e.g., "$1") so the shell handles empty or special values safely and satisfies
ShellCheck; update the if line in packaging/rpm/postinstall.sh where the snippet
reads if [ $1 -ge 1 ]; then to use the quoted positional parameter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 18b28578-4b07-4127-a687-554338d082cb

📥 Commits

Reviewing files that changed from the base of the PR and between d2aa2c0 and 7c1f55c.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (46)
  • .goreleaser.yaml
  • Makefile
  • NOTICE.txt
  • docs/development/running-locally.md
  • docs/installation/configuration.md
  • docs/installation/systemd.md
  • e2e/whole_cluster_test.go
  • go.mod
  • lima/Makefile
  • lima/ansible.cfg
  • lima/debian-13-template.yaml
  • lima/deploy.yaml
  • lima/rocky-9-template.yaml
  • lima/roles/deb_prerequisites/tasks/main.yaml
  • lima/roles/deb_prerequisites/templates/pgedge-old.sources.tmpl
  • lima/roles/deb_prerequisites/vars/main.yaml
  • lima/roles/rhel_prerequisites/tasks/main.yaml
  • lima/roles/rhel_prerequisites/vars/main.yaml
  • lima/roles/start_vms/tasks/main.yaml
  • lima/ubuntu-24.04-template.yaml
  • lima/vars.yaml
  • packaging/deb/postinstall.sh
  • packaging/preremove.sh
  • packaging/rpm/postinstall.sh
  • server/internal/config/config.go
  • server/internal/orchestrator/common/pgbackrest_config.go
  • server/internal/orchestrator/swarm/orchestrator.go
  • server/internal/orchestrator/swarm/pgbackrest_config.go
  • server/internal/orchestrator/systemd/apt.go
  • server/internal/orchestrator/systemd/apt_test.go
  • server/internal/orchestrator/systemd/dnf.go
  • server/internal/orchestrator/systemd/dnf_test.go
  • server/internal/orchestrator/systemd/golden_test/TestUnitOptions/PatroniUnitOptions/cpu_limit.service
  • server/internal/orchestrator/systemd/golden_test/TestUnitOptions/PatroniUnitOptions/fractional_cpu_limit.service
  • server/internal/orchestrator/systemd/golden_test/TestUnitOptions/PatroniUnitOptions/memory_max.service
  • server/internal/orchestrator/systemd/golden_test/TestUnitOptions/PatroniUnitOptions/minimal.service
  • server/internal/orchestrator/systemd/memory.go
  • server/internal/orchestrator/systemd/memory_test.go
  • server/internal/orchestrator/systemd/orchestrator.go
  • server/internal/orchestrator/systemd/packages.go
  • server/internal/orchestrator/systemd/packages_test.go
  • server/internal/orchestrator/systemd/patroni_unit.go
  • server/internal/orchestrator/systemd/provide.go
  • server/internal/orchestrator/systemd/unit_options_test.go
  • server/internal/pgbackrest/config.go
  • server/internal/pgbackrest/config_test.go
💤 Files with no reviewable changes (2)
  • NOTICE.txt
  • go.mod

Comment thread docs/installation/systemd.md Outdated
Comment thread docs/installation/systemd.md Outdated
Comment thread docs/installation/systemd.md Outdated
Comment thread e2e/whole_cluster_test.go
Comment thread lima/roles/deb_prerequisites/tasks/main.yaml
Comment thread lima/roles/deb_prerequisites/templates/pgedge-old.sources.tmpl
Comment thread server/internal/orchestrator/systemd/memory_test.go Outdated
Comment thread server/internal/orchestrator/systemd/patroni_unit.go Outdated
Comment thread server/internal/orchestrator/systemd/provide.go
@jason-lynch jason-lynch force-pushed the feat/PLAT-579/debian-support-ii branch 3 times, most recently from 925a7ef to a703265 Compare May 9, 2026 15:01
@jason-lynch jason-lynch marked this pull request as ready for review May 9, 2026 15:47
Adds package scripts to manage the control plane unit during
upgrades and removal. The upgrade scriptlet restarts the control plane
service after the upgrade and the removal scriptlet stops and disables
the control plane before the control plane package is removed.
We removed the replica check from this test to fix an incompatibility
with spock 5.0.7. This commit re-adds that check using polling to wait
for the replica to catch up rather than calling `wait_for_sync_event`.
This package does not require CGO on Linux, but it does require it on
macOS. This causes problems with debugging tests inside the the systemd
package.

This commit adds a simple mechanism to read memory information from
`/proc/meminfo`, which will work on all Linux distributions.
pgBackRest uses this directory to prevent multiple pgBackRest instances
from operating on the database at the same time. The default lock
directory is `/tmp/pgbackrest`, so multiple instances on the same host
could interfere with each other. This was not an issue in Swarm because
`/tmp` is an ephemeral directory inside the container, but for systemd
clusters this is an issue.

This commit resolves this issue by computing an instance-specific lock
directory when we're generating the pgBackRest configuration.
Unlike the RPM packages, the Postgres deb packages do not use a well
known UID or GID for the postgres user. Instead they use the next
available UID/GID, and the GID does not necessarily match the UID.

This commit makes it so that we look up the UID and GID of the postgres
user at runtime in systemd clusters. It also adds a separate override
configuration for the database owner GID.

This does also remove the default database owner UID from the config
file, but it retains the current behavior for Swarm by moving the
default value into the Swarm orchestrator package.

PLAT-579
Adds support for Debian-based operating systems by adding an new
`PackageManager` implementation for `apt` as well as a mechanism to
detect the OS family at startup.

PLAT-579
Adds the ability to deploy either Ubuntu 24.04 or Debian 13 when running
`make dev-lima-deploy`:

```
make dev-lima-deploy DEV_LIMA_OS=ubuntu-24.04

```

Or

```
make dev-lima-deploy DEV_LIMA_OS=debian-13
```

PLAT-579
Adds a `.deb` package to our GitHub releases.

PLAT-579
Adds the new `database_owner_gid` field to the configuration guide and
updates the description for `database_owner_uid` to match the new
behavior.

PLAT-579
@jason-lynch jason-lynch force-pushed the feat/PLAT-512/update-database-version-ii branch from d2aa2c0 to 8c65137 Compare May 11, 2026 11:50
@jason-lynch jason-lynch force-pushed the feat/PLAT-579/debian-support-ii branch 2 times, most recently from 5fdbc15 to da5f81c Compare May 11, 2026 15:00
Adds instructions for installing the Control Plane for systemd onto
debian-based hosts.

PLAT-579
@jason-lynch jason-lynch force-pushed the feat/PLAT-579/debian-support-ii branch from da5f81c to a97d5b3 Compare May 11, 2026 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant