Loading pyinfra/lib/scripts/import_scenarios.py +69 −31 Original line number Diff line number Diff line Loading @@ -64,6 +64,33 @@ def get_platform_ctrl_ip(kubeconfig): return None def wait_for_platform_ctrl_ready(cluster_ip, timeout_seconds=900, poll_interval=10): """ Waits up to `timeout_seconds` for the meep-platform-ctrl HTTP API to become ready and responsive. This is critical on fresh nodes where database images (PostGIS, Redis, CouchDB) take several minutes to download before meep-platform-ctrl pod can start. """ url = f"http://{cluster_ip}/platform-ctrl/v1/scenarios" print(f"[Scenario Import] Waiting up to {timeout_seconds}s for meep-platform-ctrl API ({url}) to become ready...") start_time = time.time() attempt = 0 while time.time() - start_time < timeout_seconds: attempt += 1 try: req = urllib.request.Request(url, headers={"Accept": "application/json"}, method="GET") with urllib.request.urlopen(req, timeout=5) as resp: if resp.status == 200: print(f"[Scenario Import] meep-platform-ctrl API is READY! (Attempt {attempt}, elapsed {int(time.time() - start_time)}s)") return True except Exception: pass if attempt % 6 == 1: print(f"[Scenario Import] Waiting for meep-platform-ctrl API to start (elapsed {int(time.time() - start_time)}s / {timeout_seconds}s)...") time.sleep(poll_interval) print(f"[Scenario Import ERROR] meep-platform-ctrl API did not become ready after {timeout_seconds}s.", file=sys.stderr) return False def get_existing_scenarios(cluster_ip): """ Sends a GET request to http://<cluster_ip>/platform-ctrl/v1/scenarios Loading Loading @@ -92,6 +119,9 @@ def import_scenario(cluster_ip, filepath, existing_names): try: with open(filepath, "r", encoding="utf-8") as f: sc_data = yaml.safe_load(f) except Exception as e: print(f"[Scenario Import] Could not read YAML file '{filepath}': {e}", file=sys.stderr) return False if not isinstance(sc_data, dict): print( Loading @@ -104,6 +134,8 @@ def import_scenario(cluster_ip, filepath, existing_names): payload = json.dumps(sc_data).encode("utf-8") url = f"http://{cluster_ip}/platform-ctrl/v1/scenarios/{basename}" for attempt in range(1, 4): try: req = urllib.request.Request( url, data=payload, Loading @@ -113,21 +145,23 @@ def import_scenario(cluster_ip, filepath, existing_names): with urllib.request.urlopen(req, timeout=15) as response: print(f"[Scenario Import] Successfully imported scenario '{basename}' (HTTP {response.status}).") return True except urllib.error.HTTPError as e: error_body = e.read().decode("utf-8", errors="ignore") if e.code in (400, 409): # Already exists or minor schema notice print(f"[Scenario Import] Notice for scenario '{basename}' (HTTP {e.code}): {error_body.strip() or e.reason}") return True if attempt == 3: print(f"[Scenario Import] Failed to import scenario '{basename}' (HTTP {e.code}): {error_body.strip() or e.reason}", file=sys.stderr) return False except Exception as e: if attempt == 3: print( f"[Scenario Import] Could not import scenario '{basename}' from '{filepath}': {e}", file=sys.stderr, ) return False time.sleep(3) def main(): Loading Loading @@ -165,6 +199,10 @@ def main(): print("[Scenario Import ERROR] Could not resolve meep-platform-ctrl ClusterIP.", file=sys.stderr) sys.exit(1) print(f"[Scenario Import] Resolved meep-platform-ctrl ClusterIP: {cluster_ip}.") if not wait_for_platform_ctrl_ready(cluster_ip, timeout_seconds=900, poll_interval=10): sys.exit(1) print(f"[Scenario Import] Connected to meep-platform-ctrl at IP: {cluster_ip}.") existing_names = get_existing_scenarios(cluster_ip) print(f"[Scenario Import] Found {len(existing_names)} existing scenario(s) on platform-ctrl.") Loading Loading
pyinfra/lib/scripts/import_scenarios.py +69 −31 Original line number Diff line number Diff line Loading @@ -64,6 +64,33 @@ def get_platform_ctrl_ip(kubeconfig): return None def wait_for_platform_ctrl_ready(cluster_ip, timeout_seconds=900, poll_interval=10): """ Waits up to `timeout_seconds` for the meep-platform-ctrl HTTP API to become ready and responsive. This is critical on fresh nodes where database images (PostGIS, Redis, CouchDB) take several minutes to download before meep-platform-ctrl pod can start. """ url = f"http://{cluster_ip}/platform-ctrl/v1/scenarios" print(f"[Scenario Import] Waiting up to {timeout_seconds}s for meep-platform-ctrl API ({url}) to become ready...") start_time = time.time() attempt = 0 while time.time() - start_time < timeout_seconds: attempt += 1 try: req = urllib.request.Request(url, headers={"Accept": "application/json"}, method="GET") with urllib.request.urlopen(req, timeout=5) as resp: if resp.status == 200: print(f"[Scenario Import] meep-platform-ctrl API is READY! (Attempt {attempt}, elapsed {int(time.time() - start_time)}s)") return True except Exception: pass if attempt % 6 == 1: print(f"[Scenario Import] Waiting for meep-platform-ctrl API to start (elapsed {int(time.time() - start_time)}s / {timeout_seconds}s)...") time.sleep(poll_interval) print(f"[Scenario Import ERROR] meep-platform-ctrl API did not become ready after {timeout_seconds}s.", file=sys.stderr) return False def get_existing_scenarios(cluster_ip): """ Sends a GET request to http://<cluster_ip>/platform-ctrl/v1/scenarios Loading Loading @@ -92,6 +119,9 @@ def import_scenario(cluster_ip, filepath, existing_names): try: with open(filepath, "r", encoding="utf-8") as f: sc_data = yaml.safe_load(f) except Exception as e: print(f"[Scenario Import] Could not read YAML file '{filepath}': {e}", file=sys.stderr) return False if not isinstance(sc_data, dict): print( Loading @@ -104,6 +134,8 @@ def import_scenario(cluster_ip, filepath, existing_names): payload = json.dumps(sc_data).encode("utf-8") url = f"http://{cluster_ip}/platform-ctrl/v1/scenarios/{basename}" for attempt in range(1, 4): try: req = urllib.request.Request( url, data=payload, Loading @@ -113,21 +145,23 @@ def import_scenario(cluster_ip, filepath, existing_names): with urllib.request.urlopen(req, timeout=15) as response: print(f"[Scenario Import] Successfully imported scenario '{basename}' (HTTP {response.status}).") return True except urllib.error.HTTPError as e: error_body = e.read().decode("utf-8", errors="ignore") if e.code in (400, 409): # Already exists or minor schema notice print(f"[Scenario Import] Notice for scenario '{basename}' (HTTP {e.code}): {error_body.strip() or e.reason}") return True if attempt == 3: print(f"[Scenario Import] Failed to import scenario '{basename}' (HTTP {e.code}): {error_body.strip() or e.reason}", file=sys.stderr) return False except Exception as e: if attempt == 3: print( f"[Scenario Import] Could not import scenario '{basename}' from '{filepath}': {e}", file=sys.stderr, ) return False time.sleep(3) def main(): Loading Loading @@ -165,6 +199,10 @@ def main(): print("[Scenario Import ERROR] Could not resolve meep-platform-ctrl ClusterIP.", file=sys.stderr) sys.exit(1) print(f"[Scenario Import] Resolved meep-platform-ctrl ClusterIP: {cluster_ip}.") if not wait_for_platform_ctrl_ready(cluster_ip, timeout_seconds=900, poll_interval=10): sys.exit(1) print(f"[Scenario Import] Connected to meep-platform-ctrl at IP: {cluster_ip}.") existing_names = get_existing_scenarios(cluster_ip) print(f"[Scenario Import] Found {len(existing_names)} existing scenario(s) on platform-ctrl.") Loading