Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ CLUSTER_TEST_IMAGE_TAG ?=
CLUSTER_TEST_DATA_DIR ?=
DEV_LIMA_OS ?= rocky-9
DEV_LIMA_USE_STAGING_PACKAGES ?= false
DEV_LIMA_SPOCK_MAJOR ?= 50

ifeq ($(DEV_IMAGE_MANIFEST),local)
dev_manifest_vars=DEV_MANIFEST_PATH=/version-manifest.json \
Expand Down Expand Up @@ -440,7 +441,8 @@ api-docs:
dev-lima-deploy:
$(MAKE) -C lima deploy \
DEV_LIMA_OS=$(DEV_LIMA_OS) \
DEV_LIMA_USE_STAGING_PACKAGES=$(DEV_LIMA_USE_STAGING_PACKAGES)
DEV_LIMA_USE_STAGING_PACKAGES=$(DEV_LIMA_USE_STAGING_PACKAGES) \
DEV_LIMA_SPOCK_MAJOR=$(DEV_LIMA_SPOCK_MAJOR)

.PHONY: dev-lima-build
dev-lima-build:
Expand Down
2 changes: 1 addition & 1 deletion changes/unreleased/Added-20260827-140429.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
kind: Added
body: Added foundational support for Spock 6 on Docker Swarm, available as a preview image via the version manifest. Databases running Spock 6 or later on Postgres 17 or later automatically use Postgres's native replication slot failover.
body: Added foundational support for Spock 6 as a preview on both Docker Swarm (via a preview image in the version manifest) and systemd (via `spock60` packages from the pgEdge repositories). Databases running Spock 6 or later on Postgres 17 or later automatically use Postgres's native replication slot failover.
time: 2026-08-27T14:04:29.000000+00:00
37 changes: 25 additions & 12 deletions docs/using/image-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,18 +266,17 @@ value in `orchestrator_opts.swarm.image` when creating the database:

### Spock 6 Preview Images

Spock 6 is available as a preview manifest entry (`"stability": "dev"`
in the version manifest), currently paired with Postgres 18.6. Spock 6
itself supports Postgres 15 through 19; the version manifest currently
offers only this one Postgres 18.6 pairing as a preview. This preview
is available for Docker Swarm deployments only; the systemd
orchestrator does not currently support Spock 6 packages.

Preview entries are excluded from image upgrades (see below) and are
never chosen for a database that omits `postgres_version` and
`spock_version`. To create a new database on the Spock 6 preview
image, set `postgres_version` and `spock_version` to match the
manifest entry:
Spock 6 is available as a preview on both Docker Swarm and systemd
deployments. Spock 6 itself supports Postgres 15 through 19.

On Docker Swarm, Spock 6 is available as a preview manifest entry
(`"stability": "dev"` in the version manifest), currently paired with
Postgres 18.6; the version manifest currently offers only this one
Postgres 18.6 pairing as a preview. Preview entries are excluded from
image upgrades (see below) and are never chosen for a database that
omits `postgres_version` and `spock_version`. To create a new database
on the Spock 6 preview image, set `postgres_version` and
`spock_version` to match the manifest entry:

=== "curl"

Expand Down Expand Up @@ -310,6 +309,20 @@ an image other than the manifest default for that version pair, such as
pinning a specific build. See
[Using a Custom Image](#using-a-custom-image).

On systemd, there's no version manifest or image to pin — install the
Spock 6 packages (`pgedge-spock60_<postgres-major>` on RPM-based
distributions, `pgedge-postgresql-<postgres-major>-spock60` on
Debian/Ubuntu) from the pgEdge repositories alongside the matching
`pgedge-postgresql*` package, following the same steps as
[Installing via systemd](../installation/systemd-installation.md).
Once installed, request the preview the same way, setting
`spock_version` to `"6"`. Unlike the Swarm manifest, systemd has no
automatic exclusion of preview versions from a database that omits
`spock_version` — a host advertises whatever Spock major/Postgres
major combinations it has packages installed for, and the highest
combination becomes that host's default. Only install the Spock 6
packages on hosts where you intend to opt into the preview.

!!! note

The Spock 6 preview image tracks the latest Spock 6 build against
Expand Down
37 changes: 25 additions & 12 deletions e2e/spock6_add_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ import (
const spock6DevImage = "ghcr.io/pgedge/pgedge-postgres:18-spock6-standard"

// TestSpock6AddNode validates the add-node workflow end-to-end against a
// real Spock 6 cluster: creates a 2-node database pinned to a Spock 6 dev
// image via orchestrator_opts.swarm.image (bypassing manifest version
// constraints, since spock6 manifest entries are deliberately "dev"
// stability and never auto-selected), adds a 3rd node, and confirms the
// full mesh reaches "replicating" — exercising the Spock-major-gated
// spock.progress query (PeerCatchupResource).
// real Spock 6 cluster, adds a 3rd node, and confirms the full mesh reaches
// "replicating" — exercising the Spock-major-gated spock.progress query
// (PeerCatchupResource).
//
// On Docker Swarm, Spock 6 is only available as a "dev" stability manifest
// entry that's never auto-selected, so the database is pinned to a Spock 6
// dev image via orchestrator_opts.swarm.image, bypassing manifest version
// constraints. On systemd there's no manifest or per-node image pin — the
// Spock 6 packages just need to be installed on the host, discovered by
// version, and requested via spock_version; postgres_version is left for the
// server to default so the test doesn't need to track exact installed
// package versions.
func TestSpock6AddNode(t *testing.T) {
t.Parallel()

Expand All @@ -40,21 +46,28 @@ func TestSpock6AddNode(t *testing.T) {

hostIDs := fixture.HostIDs()

var postgresVersion *string
var orchestratorOpts *controlplane.OrchestratorOpts
if fixture.Orchestrator() != "systemd" {
postgresVersion = pointerTo("18.6")
orchestratorOpts = &controlplane.OrchestratorOpts{
Swarm: &controlplane.SwarmOpts{Image: pointerTo(spock6DevImage)},
}
}

nodeSpec := func(name, hostID string) *controlplane.DatabaseNodeSpec {
return &controlplane.DatabaseNodeSpec{
Name: name,
HostIds: []controlplane.Identifier{controlplane.Identifier(hostID)},
OrchestratorOpts: &controlplane.OrchestratorOpts{
Swarm: &controlplane.SwarmOpts{Image: pointerTo(spock6DevImage)},
},
Name: name,
HostIds: []controlplane.Identifier{controlplane.Identifier(hostID)},
OrchestratorOpts: orchestratorOpts,
}
}

t.Log("Step 1: Creating 2-node Spock 6 database fixture")
db := fixture.NewDatabaseFixture(ctx, t, &controlplane.CreateDatabaseRequest{
Spec: &controlplane.DatabaseSpec{
DatabaseName: dbName,
PostgresVersion: pointerTo("18.6"),
PostgresVersion: postgresVersion,
SpockVersion: pointerTo("6"),
Port: pointerTo(0),
PatroniPort: pointerTo(0),
Expand Down
8 changes: 7 additions & 1 deletion lima/Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
DEV_LIMA_OS ?= rocky-9
DEV_LIMA_USE_STAGING_PACKAGES ?= false
# Spock major installed on the fixture (e.g. "50" or "60"). Spock major
# packages conflict with each other at the package-manager level (a host can
# only have one Spock major installed per Postgres major), so this selects
# one for the whole fixture rather than installing every major side-by-side.
DEV_LIMA_SPOCK_MAJOR ?= 50

ansible_playbook=ansible-playbook \
--extra-vars='@vars.yaml' \
--extra-vars='os=$(DEV_LIMA_OS)' \
--extra-vars='use_staging_packages=$(DEV_LIMA_USE_STAGING_PACKAGES)'
--extra-vars='use_staging_packages=$(DEV_LIMA_USE_STAGING_PACKAGES)' \
--extra-vars='spock_major=$(DEV_LIMA_SPOCK_MAJOR)'

.PHONY: quit
quit:
Expand Down
13 changes: 12 additions & 1 deletion lima/roles/deb_prerequisites/tasks/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,23 @@
ansible.builtin.apt:
update_cache: yes
changed_when: false
- name: Track configured Spock major for reinstall detection
ansible.builtin.copy:
content: "{{ spock_major | default('50') }}"
dest: /etc/pgedge-control-plane-dev-spock-major
register: spock_major_reconfigured
- name: Remove all known Spock packages before switching Spock major
ansible.builtin.package:
name: '{{ item }}'
state: absent
loop: '{{ pgedge_all_spock_packages }}'
when: spock_major_reconfigured.changed
- name: Remove pgEdge packages for reinstall
ansible.builtin.package:
name: '{{ item }}'
state: absent
loop: '{{ pgedge_packages }}'
when: repo_reconfigured_to_staging.changed or repo_reconfigured_to_release.changed
when: repo_reconfigured_to_staging.changed or repo_reconfigured_to_release.changed or spock_major_reconfigured.changed
- name: Install pgEdge packages
ansible.builtin.package:
name: '{{ item }}'
Expand Down
18 changes: 15 additions & 3 deletions lima/roles/deb_prerequisites/vars/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ pgedge_packages:
- pgedge-postgresql-18
- pgedge-postgresql-17
- pgedge-postgresql-16
- pgedge-postgresql-18-spock50
- pgedge-postgresql-17-spock50
- pgedge-postgresql-16-spock50
- "pgedge-postgresql-18-spock{{ spock_major | default('50') }}"
- "pgedge-postgresql-17-spock{{ spock_major | default('50') }}"
- "pgedge-postgresql-16-spock{{ spock_major | default('50') }}"
- pgedge-postgresql-18-snowflake
- pgedge-postgresql-17-snowflake
- pgedge-postgresql-16-snowflake
Expand All @@ -25,3 +25,15 @@ pgedge_packages:
- pgedge-postgresql-16-lolor
- pgedge-patroni
- pgedge-pgbackrest
# Every known Spock major's packages, regardless of which one spock_major
# currently selects. Spock majors conflict with each other at the
# package-manager level, so switching spock_major on an already-provisioned
# host requires uninstalling every other major first (see "Remove all known
# Spock packages" in tasks/main.yaml) -- add new majors here as they ship.
pgedge_all_spock_packages:
- pgedge-postgresql-18-spock50
- pgedge-postgresql-17-spock50
- pgedge-postgresql-16-spock50
- pgedge-postgresql-18-spock60
- pgedge-postgresql-17-spock60
- pgedge-postgresql-16-spock60
13 changes: 12 additions & 1 deletion lima/roles/rhel_prerequisites/tasks/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,23 @@
replace: release
when: not (use_staging_packages | bool)
register: repo_reconfigured_to_release
- name: Track configured Spock major for reinstall detection
ansible.builtin.copy:
content: "{{ spock_major | default('50') }}"
dest: /etc/pgedge-control-plane-dev-spock-major
register: spock_major_reconfigured
- name: Remove all known Spock packages before switching Spock major
ansible.builtin.package:
name: '{{ item }}'
state: absent
loop: '{{ pgedge_all_spock_packages }}'
when: spock_major_reconfigured.changed
- name: Remove pgEdge packages for reinstall
ansible.builtin.package:
name: '{{ item }}'
state: absent
loop: '{{ pgedge_packages }}'
when: repo_reconfigured_to_staging.changed or repo_reconfigured_to_release.changed
when: repo_reconfigured_to_staging.changed or repo_reconfigured_to_release.changed or spock_major_reconfigured.changed
- name: Install pgEdge packages
ansible.builtin.package:
name: '{{ item }}'
Expand Down
18 changes: 15 additions & 3 deletions lima/roles/rhel_prerequisites/vars/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ pgedge_packages:
- pgedge-postgresql18
- pgedge-postgresql17
- pgedge-postgresql16
- pgedge-spock50_18
- pgedge-spock50_17
- pgedge-spock50_16
- "pgedge-spock{{ spock_major | default('50') }}_18"
- "pgedge-spock{{ spock_major | default('50') }}_17"
- "pgedge-spock{{ spock_major | default('50') }}_16"
- pgedge-snowflake_18
- pgedge-snowflake_17
- pgedge-snowflake_16
Expand All @@ -28,3 +28,15 @@ pgedge_packages:
- pgedge-postgresql16-contrib
- pgedge-patroni
- pgedge-pgbackrest
# Every known Spock major's packages, regardless of which one spock_major
# currently selects. Spock majors conflict with each other at the
# package-manager level, so switching spock_major on an already-provisioned
# host requires uninstalling every other major first (see "Remove all known
# Spock packages" in tasks/main.yaml) -- add new majors here as they ship.
pgedge_all_spock_packages:
- pgedge-spock50_18
- pgedge-spock50_17
- pgedge-spock50_16
- pgedge-spock60_18
- pgedge-spock60_17
- pgedge-spock60_16
51 changes: 51 additions & 0 deletions server/internal/orchestrator/systemd/apt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,59 @@ func TestApt(t *testing.T) {
require.NoError(t, err)
require.Equal(t, expected, installed)
})

t.Run("installed packages with spock 6", func(t *testing.T) {
apt := systemd.Apt{
ExecCommand: systemd.MockExecCommand(t, testAptPackageListSpock60, "", nil),
}
expected := []*systemd.InstalledPostgres{
{
Postgres: &systemd.InstalledPackage{
PostgresMajor: "17",
Version: ds.MustParseVersion("17.9"),
Name: "pgedge-postgresql-17",
},
Spock: []*systemd.InstalledPackage{
{
PostgresMajor: "17",
Version: ds.MustParseVersion("5.0.7"),
Name: "pgedge-postgresql-17-spock50",
},
{
PostgresMajor: "17",
Version: ds.MustParseVersion("6.0.0"),
Name: "pgedge-postgresql-17-spock60",
},
},
},
{
Postgres: &systemd.InstalledPackage{
PostgresMajor: "18",
Version: ds.MustParseVersion("18.3"),
Name: "pgedge-postgresql-18",
},
Spock: []*systemd.InstalledPackage{
{
PostgresMajor: "18",
Version: ds.MustParseVersion("6.0.0"),
Name: "pgedge-postgresql-18-spock60",
},
},
},
}
installed, err := apt.InstalledPostgresVersions(t.Context())
require.NoError(t, err)
require.Equal(t, expected, installed)
})
}

const testAptPackageListSpock60 = `pgedge-postgresql-17 17.9-1.noble
pgedge-postgresql-17-spock50 5.0.7-1.noble
pgedge-postgresql-17-spock60 6.0.0~beta1-1.noble
pgedge-postgresql-18 18.3-1.noble
pgedge-postgresql-18-spock60 6.0.0~beta1-1.noble
`

const testAptPackageList = `adduser 3.137ubuntu1
apparmor 4.0.1really4.0.1-0ubuntu0.24.04.5
apport 2.28.1-0ubuntu3.8
Expand Down
51 changes: 51 additions & 0 deletions server/internal/orchestrator/systemd/dnf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,59 @@ func TestDnf(t *testing.T) {
require.NoError(t, err)
require.Equal(t, expected, installed)
})

t.Run("installed packages with spock 6", func(t *testing.T) {
dnf := systemd.Dnf{
ExecCommand: systemd.MockExecCommand(t, testDnfPackageListSpock60, "", nil),
}
expected := []*systemd.InstalledPostgres{
{
Postgres: &systemd.InstalledPackage{
PostgresMajor: "17",
Version: ds.MustParseVersion("17.9"),
Name: "pgedge-postgresql17",
},
Spock: []*systemd.InstalledPackage{
{
PostgresMajor: "17",
Version: ds.MustParseVersion("5.0.7"),
Name: "pgedge-spock50_17",
},
{
PostgresMajor: "17",
Version: ds.MustParseVersion("6.0.0"),
Name: "pgedge-spock60_17",
},
},
},
{
Postgres: &systemd.InstalledPackage{
PostgresMajor: "18",
Version: ds.MustParseVersion("18.3"),
Name: "pgedge-postgresql18",
},
Spock: []*systemd.InstalledPackage{
{
PostgresMajor: "18",
Version: ds.MustParseVersion("6.0.0"),
Name: "pgedge-spock60_18",
},
},
},
}
installed, err := dnf.InstalledPostgresVersions(t.Context())
require.NoError(t, err)
require.Equal(t, expected, installed)
})
}

const testDnfPackageListSpock60 = `pgedge-postgresql17 17.9-1.el9
pgedge-spock50_17 5.0.7-1.el9
pgedge-spock60_17 6.0.0-1.el9
pgedge-postgresql18 18.3-1.el9
pgedge-spock60_18 6.0.0-1.el9
`

const testDnfPackageList = `gawk-all-langpacks 5.1.0
setup 2.13.7
filesystem 3.16
Expand Down
2 changes: 1 addition & 1 deletion server/internal/orchestrator/systemd/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

var (
supportedPostgresVersions = []string{"16", "17", "18"}
supportedSpockVersions = []string{"50"}
supportedSpockVersions = []string{"50", "60"}
)

type InstalledPackage struct {
Expand Down