From 898073fd9838b1873f7f64acddc3ad3cf8130e9e Mon Sep 17 00:00:00 2001 From: moizpgedge Date: Mon, 30 Jun 2025 15:38:39 +0500 Subject: [PATCH 1/2] Spock 5 integration --- cli/scripts/cluster.py | 5 +- cli/scripts/meta.py | 1 - cli/scripts/setup.py | 3 +- cli/scripts/util.py | 120 ++++++++++++++++++++++++++++++++++++++++- src/conf/versions.sql | 22 ++------ 5 files changed, 127 insertions(+), 24 deletions(-) diff --git a/cli/scripts/cluster.py b/cli/scripts/cluster.py index a4915c4a..8f3bf6db 100755 --- a/cli/scripts/cluster.py +++ b/cli/scripts/cluster.py @@ -6,6 +6,7 @@ import meta import time import sys +import setup_core import getpass from tabulate import tabulate # type: ignore from ipaddress import ip_address @@ -1312,7 +1313,9 @@ def init(cluster_name, install=True): parsed_json = get_cluster_json(cluster_name) if parsed_json is None: util.exit_message("Unable to load cluster JSON", 1) - + pg_version = db_settings["pg_version"] + spock_ver = db_settings.get("spock_version", util.get_default_spock(pg_version)) + util.validate_spock_pg_compat(spock_ver, pg_version) verbose = parsed_json.get("log_level", "info") all_nodes = nodes.copy() diff --git a/cli/scripts/meta.py b/cli/scripts/meta.py index b399e43a..9cd5fb1c 100644 --- a/cli/scripts/meta.py +++ b/cli/scripts/meta.py @@ -306,7 +306,6 @@ def get_default_spock(pgv): + pgv + "' \n" + " AND component LIKE 'spock%'" - + " AND version not LIKE '%devel%'" ) try: c = con.cursor() diff --git a/cli/scripts/setup.py b/cli/scripts/setup.py index 2fc21aa9..6e5452d8 100755 --- a/cli/scripts/setup.py +++ b/cli/scripts/setup.py @@ -95,7 +95,8 @@ def setup_pgedge(User=None, Passwd=None, dbName=None, port=None, pg_data=None, p pg_ver = df_pg pg_major, pg_minor = setup_core.parse_pg(pg_ver) - + # Validate that if Spock ≥5 we're using PG 15.13+, 16.9+ or 17.5+Add commentMore actions + util.validate_spock_pg_compat(spock_ver, pg_ver) pg_init_options = "" if pg_data is not None: pg_data = pg_data.rstrip("/") diff --git a/cli/scripts/util.py b/cli/scripts/util.py index b4335a21..4a0ef6ea 100644 --- a/cli/scripts/util.py +++ b/cli/scripts/util.py @@ -8,8 +8,8 @@ MY_CODENAME = "" DEFAULT_PG = "16" -DEFAULT_SPOCK = "40" -DEFAULT_SPOCK_17 = "40" +DEFAULT_SPOCK = "50" +DEFAULT_SPOCK_17 = "50" MY_CMD = os.getenv("MY_CMD", None) MY_HOME = os.getenv("MY_HOME", None) MY_LIBS = f"{MY_HOME}/hub/scripts/lib" @@ -37,6 +37,7 @@ import subprocess import getpass import filecmp +import re from subprocess import Popen, PIPE, STDOUT from datetime import datetime, timedelta from urllib import request as urllib2 @@ -123,7 +124,122 @@ def get_default_spock(pgv): return(DEFAULT_SPOCK) +def get_default_spock(pgv): + if pgv == "17": + return(DEFAULT_SPOCK_17) + + return(DEFAULT_SPOCK) + + + +def validate_spock_pg_compat(spock_ver: str = None, pg_ver: str = None) -> None: + """ + Compatibility rules: + • If Spock < 5.0.0 ⇒ works with any supported PostgreSQL major. + • If Spock ≥ 5.0.0 ⇒ + – PG15 must be ≥ 15.13 + – PG16 must be ≥ 16.9 + – PG17 must be ≥ 17.5 + + Also supports shorthand Spock strings: + – "50" → "5.0.0", "40" → "4.0.0", etc. + """ + # 0) Fill in defaults if user didn’t pass anything + if not pg_ver: + pg_ver = DEFAULT_PG + if not spock_ver: + maj = int(pg_ver.split(".", 1)[0]) + spock_ver = DEFAULT_SPOCK_17 if maj == 17 else DEFAULT_SPOCK + + # 0.5) Normalize two-digit shorthand (e.g. "50" → "5.0.0") + m = re.fullmatch(r'(\d)(\d)$', spock_ver) + if m: + spock_ver = f"{int(m.group(1))}.{int(m.group(2))}.0" + + # 1) Parse Spock version (abort on bad format) + try: + spv = Version(spock_ver) + except ValueError: + exit_message(f"Invalid Spock version '{spock_ver}'. Aborting.", 1, isJSON) + + # 2) If Spock < 5 ⇒ compatible with any PG + if spv.major < 5: + return + # — New block: handle pg_ver with “-1” or “-2” suffix + rev = None + rev_match = re.fullmatch(r'(\d+)\.(\d+)-(1|2)$', pg_ver) + if rev_match: + pg_major = int(rev_match.group(1)) + pg_patch = int(rev_match.group(2)) + rev = int(rev_match.group(3)) + + # reject revision “-1” on Spock ≥5 + if rev == 1: + exit_message( + f"Error: PostgreSQL {pg_major}.{pg_patch}-1 is not supported with Spock {spv}; " + "please use the “-2” revision instead.", + 1, + isJSON + ) + # for “-2”, we strip suffix and proceed with pg_major/pg_patch below + # end new block + + # 3) Spock ≥ 5 ⇒ enforce minimum‐patch for each PG major + minimum_patches = { + 15: 13, + 16: 9, + 17: 5, + } + + # 4) Extract PG major and patch (if not already set by rev_match) + if rev_match: + # pg_major, pg_patch are already set + pass + elif "." not in pg_ver: + # bare-major → use its minimum patch + try: + pg_major = int(pg_ver) + except ValueError: + exit_message(f"Invalid PostgreSQL version '{pg_ver}'. Aborting.", 1, isJSON) + if pg_major not in minimum_patches: + allowed = ", ".join(str(m) for m in minimum_patches) + exit_message( + f"Error: Spock {spv} supports only PostgreSQL majors {allowed}; " + f"you have {pg_major}. Aborting.", + 1, + isJSON + ) + pg_patch = minimum_patches[pg_major] + else: + parts = pg_ver.split(".", 2) + if len(parts) < 2: + exit_message(f"Invalid PostgreSQL version '{pg_ver}'. Aborting.", 1, isJSON) + try: + pg_major = int(parts[0]) + pg_patch = int(parts[1]) + except ValueError: + exit_message(f"Invalid PostgreSQL version '{pg_ver}'. Aborting.", 1, isJSON) + + # 5) Major must be supported + if pg_major not in minimum_patches: + allowed = ", ".join(str(m) for m in minimum_patches) + exit_message( + f"Error: Spock {spv} supports only PostgreSQL majors {allowed}; " + f"you have {pg_major}. Aborting.", + 1, + isJSON + ) + + # 6) Enforce minimum‐patch + required = minimum_patches[pg_major] + if pg_patch < required: + exit_message( + f"Error: Spock {spv} requires PostgreSQL {pg_major}.{required} or newer; " + f"you have {pg_major}.{pg_patch}. Aborting.", + 1, + isJSON + ) def get_cpu_info(): try: import cpuinfo diff --git a/src/conf/versions.sql b/src/conf/versions.sql index 36a51883..2e09f6d5 100644 --- a/src/conf/versions.sql +++ b/src/conf/versions.sql @@ -74,11 +74,6 @@ CREATE TABLE extensions ( preload_name TEXT NOT NULL, default_conf TEXT NOT NULL ); -INSERT INTO extensions VALUES ('spock33', 'spock', 1, 'spock', - 'wal_level=logical | max_worker_processes=12 | max_replication_slots=16 | - max_wal_senders=16 | hot_standby_feedback=on | wal_sender_timeout=5s | - track_commit_timestamp=on | spock.conflict_resolution=last_update_wins | - spock.save_resolutions=on | spock.conflict_log_level=DEBUG'); INSERT INTO extensions VALUES ('spock40', 'spock', 1, 'spock', 'wal_level=logical | max_worker_processes=12 | max_replication_slots=16 | max_wal_senders=16 | hot_standby_feedback=on | wal_sender_timeout=5s | @@ -320,17 +315,6 @@ INSERT INTO versions VALUES ('snowflake-pg17', '2.2-1', 'amd, arm', 1, '20240626 -- ## SPOCK (parent project) ############ INSERT INTO projects VALUES ('spock', 'pge', 4, 0, '', 1, 'https://github.com/pgedge/spock/tags', 'spock', 1, 'spock.png', 'Logical Rep w/ Conflict Resolution', 'https://github.com/pgedge/spock/', 'pg_spock, pgsspock, vulcan'); - --- ## SPOCK33 ########################### -INSERT INTO releases VALUES ('spock33-pg15', 4, 'spock', 'Spock', '', 'prod', '', 1, 'pgEdge Community', '', ''); -INSERT INTO releases VALUES ('spock33-pg16', 4, 'spock', 'Spock', '', 'prod', '', 1, 'pgEdge Community', '', ''); - -INSERT INTO versions VALUES ('spock33-pg15', '3.3.6-1', 'amd, arm', 1, '20240820', 'pg15', '', ''); -INSERT INTO versions VALUES ('spock33-pg16', '3.3.6-1', 'amd, arm', 1, '20240820', 'pg16', '', ''); - -INSERT INTO versions VALUES ('spock33-pg15', '3.3.5-1', 'amd, arm', 0, '20240607', 'pg15', '', ''); -INSERT INTO versions VALUES ('spock33-pg16', '3.3.5-1', 'amd, arm', 0, '20240607', 'pg16', '', ''); - -- ## SPOCK40 ########################### INSERT INTO releases VALUES ('spock40-pg15', 4, 'spock', 'Spock', '', 'prod', '', 1, 'pgEdge Community', '', ''); INSERT INTO releases VALUES ('spock40-pg16', 4, 'spock', 'Spock', '', 'prod', '', 1, 'pgEdge Community', '', ''); @@ -350,9 +334,9 @@ INSERT INTO versions VALUES ('spock40-pg17', '4.0.8-1', 'amd, arm', 0, '20241218 -- ## spock50 ########################### -INSERT INTO releases VALUES ('spock50-pg15', 4, 'spock', 'Spock', '', 'test', '', 1, 'pgEdge Community', '', ''); -INSERT INTO releases VALUES ('spock50-pg16', 4, 'spock', 'Spock', '', 'test', '', 1, 'pgEdge Community', '', ''); -INSERT INTO releases VALUES ('spock50-pg17', 4, 'spock', 'Spock', '', 'test', '', 1, 'pgEdge Community', '', ''); +INSERT INTO releases VALUES ('spock50-pg15', 4, 'spock', 'Spock', '', 'prod', '', 1, 'pgEdge Community', '', ''); +INSERT INTO releases VALUES ('spock50-pg16', 4, 'spock', 'Spock', '', 'prod', '', 1, 'pgEdge Community', '', ''); +INSERT INTO releases VALUES ('spock50-pg17', 4, 'spock', 'Spock', '', 'prod', '', 1, 'pgEdge Community', '', ''); INSERT INTO versions VALUES ('spock50-pg15', '5.0.0-devel1-1', 'amd, arm', 1, '20250521', 'pg15', '', ''); INSERT INTO versions VALUES ('spock50-pg16', '5.0.0-devel1-1', 'amd, arm', 1, '20250521', 'pg16', '', ''); From 32d523351974aba0d8e0eb82e45c7934bc202731 Mon Sep 17 00:00:00 2001 From: Moiz Ibrar Date: Mon, 30 Jun 2025 20:21:03 +0500 Subject: [PATCH 2/2] function duplication removed from util.py --- cli/scripts/util.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cli/scripts/util.py b/cli/scripts/util.py index 4a0ef6ea..22f1e5f4 100644 --- a/cli/scripts/util.py +++ b/cli/scripts/util.py @@ -124,12 +124,6 @@ def get_default_spock(pgv): return(DEFAULT_SPOCK) -def get_default_spock(pgv): - if pgv == "17": - return(DEFAULT_SPOCK_17) - - return(DEFAULT_SPOCK) - def validate_spock_pg_compat(spock_ver: str = None, pg_ver: str = None) -> None: