"""Shared routing logic for generate_loki_config.py and generate_vm_config.py. Provides meta.json-based dataplane resolution: parses region IDs, builds biz strings for route pattern matching, and resolves ordered candidate dataplaneAddresses per region. """ import json import re from collections import defaultdict from pathlib import Path SUFFIX_RE = re.compile(r"-(ng|lyc|pov|def|e2e)$", re.IGNORECASE) FNUM_RE = re.compile(r"-f\d{2}$", re.IGNORECASE) # ── parsing ──────────────────────────────────────────────────────────────── def parse_region_id(rid): parts = rid.split(":") if len(parts) != 4: return None arch, prov, env, dirname = parts if not all((arch, prov, env, dirname)): return None if arch == "dedicated": pfx = f"{prov}-{env}-" if not dirname.startswith(pfx): return None return dict( region_id=rid, architecture=arch, provider=prov, environment=env, region_name=dirname[len(pfx):], ) pfxs = ["ali", "alicloud"] if prov == "alicloud" else [prov] for p in pfxs: pfx = f"{env}-{p}-" if dirname.startswith(pfx): return dict( region_id=rid, architecture=arch, provider=prov, environment=env, region_name=dirname[len(pfx):], ) return None # ── meta.json routing ────────────────────────────────────────────────────── def make_biz_string(architecture, environment): if architecture == "dedicated": if environment == "prod": return "tidbcloud:x:x" return f"{environment}-tidbcloud:x:x" if architecture in ("nextgen", "byoc"): # byoc rides the nextgen routing until meta.json grows byoc patterns. return f"{environment}-tidbcloud-nextgen:x:x" if architecture == "serverless": return f"{environment}-devtier:x:x" return f"{architecture}:x:x" def physical_region_keys(region_name): seen, result = set(), [] def add(v): if v and v not in seen: seen.add(v) result.append(v) add(region_name) add(SUFFIX_RE.sub("", region_name)) add(FNUM_RE.sub("", region_name)) add(FNUM_RE.sub("", SUFFIX_RE.sub("", region_name))) for sfx in ["-byoc-poc", "-na-test", "-test"]: if region_name.endswith(sfx): add(region_name[:-len(sfx)]) cursor = region_name while "-" in cursor: cursor = cursor.rsplit("-", 1)[0] add(cursor) return result def load_meta(path): meta = json.loads(path.read_text()) router_map = {} primary_by_region = {} physical_regions = defaultdict(set) for vendor, regions in meta.get("vendors", {}).items(): for r in regions: phys = r["region"] physical_regions[vendor].add(phys) for router in r.get("routers", []): lrn = router.get("logicalRegionName", "") dp = router.get("dataplaneAddress", "") if lrn and dp: router_map[(vendor, lrn)] = dp if router.get("primary"): primary_by_region[(vendor, phys)] = dict( dataplaneAddress=dp, logicalRegionName=lrn, ) routes = meta.get("routes", {}) return router_map, primary_by_region, routes, dict(physical_regions) def _pattern_specificity(pattern): score = 0 for part in pattern.split(":"): if part != ".*": score += 10 score += 1 return score def _resolve_routed_base(region, router_map, routes, phys_regions): provider = region["provider"] region_name = region["region_name"] if region_name not in phys_regions.get(provider, set()): dp = router_map.get((provider, region_name)) if dp: return dp biz = make_biz_string(region["architecture"], region["environment"]) for rk in physical_region_keys(region_name): route_patterns = routes.get(f"{provider}-{rk}") if not route_patterns: continue best_logical, best_spec = None, -1 for pattern, logical_name in route_patterns.items(): if re.fullmatch(pattern, biz): spec = _pattern_specificity(pattern) if spec > best_spec: best_spec = spec best_logical = logical_name if best_logical: dp = router_map.get((provider, best_logical)) if dp: return dp return None def _resolve_primary_base(region, primary_by_region): for rk in physical_region_keys(region["region_name"]): primary = primary_by_region.get((region["provider"], rk)) if primary: return primary["dataplaneAddress"] return None def resolve_candidate_bases(region, router_map, primary_by_region, routes, phys_regions): """Return ordered candidate dataplaneAddresses: routed first, primary fallback.""" candidates = [] seen = set() routed = _resolve_routed_base(region, router_map, routes, phys_regions) if routed: candidates.append(routed) seen.add(routed) primary = _resolve_primary_base(region, primary_by_region) if primary and primary not in seen: candidates.append(primary) return candidates or None