diff --git a/Makefile b/Makefile index 994f9d61..a3c09c24 100644 --- a/Makefile +++ b/Makefile @@ -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 \ @@ -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: diff --git a/changes/unreleased/Added-20260827-140429.yaml b/changes/unreleased/Added-20260827-140429.yaml index e24f3d42..0b65e07d 100644 --- a/changes/unreleased/Added-20260827-140429.yaml +++ b/changes/unreleased/Added-20260827-140429.yaml @@ -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 diff --git a/docs/using/image-management.md b/docs/using/image-management.md index 14e11fa8..53082354 100644 --- a/docs/using/image-management.md +++ b/docs/using/image-management.md @@ -297,19 +297,17 @@ value in `orchestrator_opts.swarm.image` when creating the database: ### Spock 6 Preview Images -Spock 6 is available as a preview manifest entry with -[`"stability": "dev"`](#image-stability), 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 with +[`"stability": "dev"`](#image-stability), 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" @@ -342,6 +340,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_` on RPM-based +distributions, `pgedge-postgresql--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 diff --git a/e2e/spock6_add_node_test.go b/e2e/spock6_add_node_test.go index 03940a3c..be08ef2d 100644 --- a/e2e/spock6_add_node_test.go +++ b/e2e/spock6_add_node_test.go @@ -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() @@ -40,13 +46,20 @@ 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, } } @@ -54,7 +67,7 @@ func TestSpock6AddNode(t *testing.T) { 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), diff --git a/lima/Makefile b/lima/Makefile index 7467890f..a689e39f 100644 --- a/lima/Makefile +++ b/lima/Makefile @@ -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: diff --git a/lima/roles/deb_prerequisites/tasks/main.yaml b/lima/roles/deb_prerequisites/tasks/main.yaml index 19b6bd78..acde521b 100644 --- a/lima/roles/deb_prerequisites/tasks/main.yaml +++ b/lima/roles/deb_prerequisites/tasks/main.yaml @@ -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 }}' diff --git a/lima/roles/deb_prerequisites/vars/main.yaml b/lima/roles/deb_prerequisites/vars/main.yaml index 9143b563..c6c5416d 100644 --- a/lima/roles/deb_prerequisites/vars/main.yaml +++ b/lima/roles/deb_prerequisites/vars/main.yaml @@ -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 @@ -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 diff --git a/lima/roles/rhel_prerequisites/tasks/main.yaml b/lima/roles/rhel_prerequisites/tasks/main.yaml index 0acc5d8b..6950ce30 100644 --- a/lima/roles/rhel_prerequisites/tasks/main.yaml +++ b/lima/roles/rhel_prerequisites/tasks/main.yaml @@ -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 }}' diff --git a/lima/roles/rhel_prerequisites/vars/main.yaml b/lima/roles/rhel_prerequisites/vars/main.yaml index 77245042..e6f87d8c 100644 --- a/lima/roles/rhel_prerequisites/vars/main.yaml +++ b/lima/roles/rhel_prerequisites/vars/main.yaml @@ -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 @@ -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 diff --git a/server/internal/orchestrator/systemd/apt_test.go b/server/internal/orchestrator/systemd/apt_test.go index 830a29c0..a79474a7 100644 --- a/server/internal/orchestrator/systemd/apt_test.go +++ b/server/internal/orchestrator/systemd/apt_test.go @@ -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 diff --git a/server/internal/orchestrator/systemd/dnf_test.go b/server/internal/orchestrator/systemd/dnf_test.go index 28fe9f3b..74978b24 100644 --- a/server/internal/orchestrator/systemd/dnf_test.go +++ b/server/internal/orchestrator/systemd/dnf_test.go @@ -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 diff --git a/server/internal/orchestrator/systemd/packages.go b/server/internal/orchestrator/systemd/packages.go index af41940b..97e7c6c8 100644 --- a/server/internal/orchestrator/systemd/packages.go +++ b/server/internal/orchestrator/systemd/packages.go @@ -13,7 +13,7 @@ import ( var ( supportedPostgresVersions = []string{"16", "17", "18"} - supportedSpockVersions = []string{"50"} + supportedSpockVersions = []string{"50", "60"} ) type InstalledPackage struct {