"""Shared helpers for reading GCP account metadata.""" from __future__ import annotations import csv from pathlib import Path from typing import Optional, Set GCP_DIR = Path(__file__).resolve().parent _TRUTHY_MARKERS = { "true", "1", "yes", "y", "on", "active", "enabled", } _TRUTHY_NATIVE = {"是", "启用"} def _normalize_flag(value: Optional[str]) -> bool: """Return True when the provided CSV field represents an enabled flag.""" if value is None: return False raw = str(value).strip() if not raw: return False lowered = raw.lower() if lowered in _TRUTHY_MARKERS: return True return raw in _TRUTHY_NATIVE def resolve_account_details_path(account_details_file: Optional[str] = None) -> Optional[Path]: """Resolve the preferred account details CSV path if one exists.""" candidates = [] if account_details_file: path = Path(account_details_file) if not path.is_absolute(): path = GCP_DIR / path candidates.append(path) # Fall back to the canonical filename, but also honour the legacy typo. candidates.extend( [ GCP_DIR / "gcp_account_details.csv", GCP_DIR / "gcp_accout_details.csv", ] ) for path in candidates: if path.exists(): return path return None def load_active_accounts(account_details_file: Optional[str] = None) -> Optional[Set[str]]: """Load the set of active GCP account identifiers from the CSV file. Returns ``None`` when the account metadata file is missing or malformed, signalling that callers should not filter the input data. """ details_path = resolve_account_details_path(account_details_file) if details_path is None: print("Warning: GCP account details file not found. Skipping account filtering.") return None active_accounts: Set[str] = set() try: with details_path.open("r", encoding="utf-8-sig") as handle: reader = csv.DictReader(handle) if not reader.fieldnames or "account_id" not in reader.fieldnames: print( "Warning: account details file missing 'account_id' column: " f"{details_path}" ) return None for row in reader: account_id = (row.get("account_id") or "").strip() if not account_id: continue flag_value = row.get("is_active") if flag_value is None: # Accept camelCase should future templates adopt it. flag_value = row.get("isActive") if _normalize_flag(flag_value): active_accounts.add(account_id) except Exception as exc: # pylint: disable=broad-except print(f"Warning: Failed to load GCP account details from {details_path}: {exc}") return None if not active_accounts: print(f"Warning: No active GCP accounts marked in {details_path}") print(f"Loaded {len(active_accounts)} active GCP accounts from {details_path.name}") return active_accounts