diff --git a/src/swarm-scripts/00-initialize-secrets.sh b/src/swarm-scripts/00-initialize-secrets.sh index bebaf4d1..4cdb62c7 100644 --- a/src/swarm-scripts/00-initialize-secrets.sh +++ b/src/swarm-scripts/00-initialize-secrets.sh @@ -30,6 +30,7 @@ BASE_DOMAIN=$(cfg "base_domain") SWARM_DOMAIN=$(cfg "swarm_domain") PKI_DOMAIN=$(cfg "pki_domain") GATEWAY_HOSTNAME=$(cfg "gateway_hostname") +GLOBAL_ID=$(cfg "global_id") AUTH_SERVICE_YAML="" AUTH_SERVICE_YAML_PATH="/sp/swarm/auth-service.yaml" @@ -68,6 +69,20 @@ ensure_secret() { python3 "$(dirname "$0")/swarm-cli.py" create SwarmSecrets "$key" --value "$value" >/dev/null } +ensure_global_id_pointer() { + local value="$1" + [ -n "$value" ] || return 0 + + if DB_HOST="$DB_HOST" DB_PORT="$DB_PORT" DB_USER="$DB_USER" DB_NAME="$DB_NAME" \ + python3 "$(dirname "$0")/swarm-cli.py" get SwarmIdPointer "global_id" >/dev/null 2>&1; then + echo "SwarmIdPointer 'global_id' already exists, skipping creation." + return 0 + fi + + DB_HOST="$DB_HOST" DB_PORT="$DB_PORT" DB_USER="$DB_USER" DB_NAME="$DB_NAME" \ + python3 "$(dirname "$0")/swarm-cli.py" create SwarmIdPointer "global_id" --value "$value" >/dev/null +} + ensure_swarm_init_cert_secrets() { local secret_key local prefixed_secret_key @@ -119,6 +134,7 @@ ensure_secret "base_domain" "$BASE_DOMAIN" ensure_secret "swarm_domain" "$SWARM_DOMAIN" ensure_secret "pki_domain" "$PKI_DOMAIN" ensure_secret "gateway_hostname" "$GATEWAY_HOSTNAME" +ensure_global_id_pointer "$GLOBAL_ID" ensure_secret "auth_service_yaml" "$AUTH_SERVICE_YAML" ensure_swarm_init_cert_secrets ensure_secret "evidence_sign_key" "$EVIDENCE_SIGN_KEY" diff --git a/src/swarm-scripts/74.setup-dns-manager.sh b/src/swarm-scripts/74.setup-dns-manager.sh index 03ed1f3f..a7540971 100755 --- a/src/swarm-scripts/74.setup-dns-manager.sh +++ b/src/swarm-scripts/74.setup-dns-manager.sh @@ -66,4 +66,22 @@ else --location="$LOCATION_PATH" fi +AFFINITY_RULE_ID="${CLUSTER_POLICY}:knot-affinity" +if DB_HOST="$DB_HOST" DB_PORT="$DB_PORT" DB_USER="$DB_USER" DB_NAME="$DB_NAME" \ + python3 "$(dirname "$0")/swarm-cli.py" get ClusterPolicies knot >/dev/null 2>&1; then + echo "Ensuring ClusterPolicyAffinityRule '$AFFINITY_RULE_ID'..." + if DB_HOST="$DB_HOST" DB_PORT="$DB_PORT" DB_USER="$DB_USER" DB_NAME="$DB_NAME" \ + python3 "$(dirname "$0")/swarm-cli.py" get ClusterPolicyAffinityRules "$AFFINITY_RULE_ID" >/dev/null 2>&1; then + echo "ClusterPolicyAffinityRule '$AFFINITY_RULE_ID' already exists, skipping creation." + else + echo "Creating ClusterPolicyAffinityRule '$AFFINITY_RULE_ID'..." + DB_HOST="$DB_HOST" DB_PORT="$DB_PORT" DB_USER="$DB_USER" DB_NAME="$DB_NAME" \ + python3 "$(dirname "$0")/swarm-cli.py" create ClusterPolicyAffinityRules "$AFFINITY_RULE_ID" \ + --name="knot-affinity" \ + --cluster_policy="$CLUSTER_POLICY" \ + --target_cluster_policy="knot" \ + --affinity_type="positive" + fi +fi + echo "Done. The provision worker will reconcile '$SERVICE_NAME' shortly." diff --git a/src/swarm-scripts/swarm-cli.py b/src/swarm-scripts/swarm-cli.py index 550e31d3..2415e366 100755 --- a/src/swarm-scripts/swarm-cli.py +++ b/src/swarm-scripts/swarm-cli.py @@ -402,6 +402,58 @@ def get_cluster_policy_preference_rules(args: argparse.Namespace, db: dict, engi print(json.dumps(dict(row))) +def create_swarm_id_pointer(args: argparse.Namespace, db: dict, engine: Engine) -> None: + """ + Insert or keep a SwarmIdPointer. + Semantics are like INSERT IGNORE: we do not overwrite existing pointers. + """ + id_value = args.id or args.positional_id + value = args.value + + if not id_value or value is None: + print("SwarmIdPointer requires id (positional or --id) and --value.", file=sys.stderr) + sys.exit(1) + + insert_sql = ( + "INSERT INTO SwarmIdPointer (id, value)\n" + "VALUES (:id, :value)\n" + "ON DUPLICATE KEY UPDATE\n" + " value = value;\n" + ) + params = { + "id": id_value, + "value": value, + } + run_sql(engine, insert_sql, params) + print(f"SwarmIdPointer '{id_value}' upserted (existing values preserved).") + + +def get_swarm_id_pointer(args: argparse.Namespace, db: dict, engine: Engine) -> None: + """ + Fetch a SwarmIdPointer by id. + - Exit code 0: pointer exists, JSON is printed to stdout. + - Exit code 1: pointer not found. + - Exit code 2: query failed. + """ + pointer_id = args.id or args.positional_id + if not pointer_id: + print("SwarmIdPointer get requires id (positional or --id).", file=sys.stderr) + sys.exit(2) + + sql = "SELECT id, value FROM SwarmIdPointer WHERE id = :id LIMIT 1" + try: + with engine.connect() as conn: + row = conn.execute(text(sql), {"id": pointer_id}).mappings().first() + except Exception as exc: + print(f"MySQL query failed: {exc}", file=sys.stderr) + sys.exit(2) + + if row is None: + sys.exit(1) + + print(json.dumps(dict(row))) + + def create_swarm_secrets(args: argparse.Namespace, db: dict, engine: Engine) -> None: """ Insert or keep a SwarmSecret. @@ -520,6 +572,7 @@ def parse_args(argv: List[str]) -> argparse.Namespace: "ClusterPolicyMeasurementRules", "ClusterPolicyPreferenceRules", "SwarmSecrets", + "SwarmIdPointer", ], ) # Positional optional id (first non-key=value), like the original script @@ -593,6 +646,10 @@ def main(argv: List[str]) -> None: create_cluster_services(args, db, engine) elif key == "create:SwarmSecrets": create_swarm_secrets(args, db, engine) + elif key == "create:SwarmIdPointer": + create_swarm_id_pointer(args, db, engine) + elif key == "get:SwarmIdPointer": + get_swarm_id_pointer(args, db, engine) elif key == "get:ClusterPolicies": get_cluster_policies(args, db, engine) elif key == "get:ClusterServices":