Coverage for scripts / example_job_validation / static_checks.py: 100.00%

175 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-09-14 22:07 +0000

1"""Offline validation of every example against its documented contract. 

2 

3Runs with no AWS access and no cluster: parses each example, checks the 

4spec registry's symmetry with the ``examples/`` directory and the 

5``gco_mcp`` catalog, and — for examples documented to travel the API/SQS 

6submission paths — proves every document clears the exact transport gates 

7(kind/GVK allowlist, image-source trust, target namespace) that the 

8deployed services enforce. This is the half that runs in CI on every PR 

9(``tests/test_example_job_validation.py``); the live half in 

10``checks/examples.py`` builds on the same parse. 

11""" 

12 

13from __future__ import annotations 

14 

15from dataclasses import dataclass, field 

16from pathlib import Path 

17from typing import Any 

18 

19import yaml 

20 

21from .specs import ( 

22 COMPANION, 

23 DAG_RUN, 

24 EXAMPLE_SPECS, 

25 SUBMISSION_PATHS, 

26 SUBMIT_API, 

27 SUBMIT_DIRECT, 

28 SUBMIT_SQS, 

29 ExampleSpec, 

30) 

31 

32#: Namespaces the platform provisions for user workloads. 

33_WORKLOAD_NAMESPACES = frozenset({"gco-jobs", "gco-inference"}) 

34 

35 

36@dataclass 

37class StaticFinding: 

38 """One offline check outcome for one example.""" 

39 

40 example: str 

41 check: str 

42 passed: bool 

43 detail: str = "" 

44 

45 

46@dataclass 

47class ParsedExample: 

48 """An example file parsed into documents plus its spec.""" 

49 

50 name: str 

51 path: Path 

52 spec: ExampleSpec 

53 documents: list[dict[str, Any]] = field(default_factory=list) 

54 

55 

56def examples_dir(repo_root: Path) -> Path: 

57 return repo_root / "examples" 

58 

59 

60def example_names(repo_root: Path) -> list[str]: 

61 return sorted(path.stem for path in examples_dir(repo_root).glob("*.yaml")) 

62 

63 

64def parse_example(repo_root: Path, name: str) -> ParsedExample: 

65 """Parse one example's YAML documents (raises on unknown name or bad YAML).""" 

66 spec = EXAMPLE_SPECS.get(name) 

67 if spec is None: 

68 raise KeyError(f"No validation spec for example {name!r} (add one in specs.py)") 

69 path = examples_dir(repo_root) / f"{name}.yaml" 

70 documents = [ 

71 doc for doc in yaml.safe_load_all(path.read_text(encoding="utf-8")) if doc is not None 

72 ] 

73 return ParsedExample(name=name, path=path, spec=spec, documents=documents) 

74 

75 

76def _catalog_metadata(repo_root: Path) -> dict[str, dict[str, Any]]: 

77 """Read ``gco_mcp``'s EXAMPLE_METADATA literal without executing the module. 

78 

79 ``gco_mcp/resources/docs.py`` imports flat sibling modules (the MCP server 

80 puts ``gco_mcp/`` itself on ``sys.path``), so importing it from here would 

81 require path surgery. The catalog is a pure literal dict, so an AST read 

82 is sufficient — and side-effect free. 

83 """ 

84 import ast 

85 

86 docs_path = repo_root / "gco_mcp" / "resources" / "docs.py" 

87 tree = ast.parse(docs_path.read_text(encoding="utf-8")) 

88 for node in ast.walk(tree): 

89 value: ast.expr | None = None 

90 targets: list[ast.expr] = [] 

91 if isinstance(node, ast.Assign): 

92 targets, value = node.targets, node.value 

93 elif isinstance(node, ast.AnnAssign) and node.value is not None: 

94 targets, value = [node.target], node.value 

95 for target in targets: 

96 if isinstance(target, ast.Name) and target.id == "EXAMPLE_METADATA": 

97 assert value is not None 

98 catalog = ast.literal_eval(value) 

99 if not isinstance(catalog, dict): 

100 raise RuntimeError("EXAMPLE_METADATA is not a dict literal") 

101 return catalog 

102 raise RuntimeError(f"EXAMPLE_METADATA literal not found in {docs_path}") 

103 

104 

105def check_registry_symmetry(repo_root: Path) -> list[StaticFinding]: 

106 """Specs, files, and the MCP catalog must describe the same example set.""" 

107 findings: list[StaticFinding] = [] 

108 files = set(example_names(repo_root)) 

109 specs = set(EXAMPLE_SPECS) 

110 catalog = set(_catalog_metadata(repo_root)) 

111 findings.append( 

112 StaticFinding( 

113 example="*", 

114 check="spec/file symmetry", 

115 passed=files == specs, 

116 detail=( 

117 f"only in examples/: {sorted(files - specs)}; only in specs: {sorted(specs - files)}" 

118 if files != specs 

119 else "" 

120 ), 

121 ) 

122 ) 

123 findings.append( 

124 StaticFinding( 

125 example="*", 

126 check="spec/catalog symmetry", 

127 passed=catalog == specs, 

128 detail=( 

129 f"only in catalog: {sorted(catalog - specs)}; only in specs: {sorted(specs - catalog)}" 

130 if catalog != specs 

131 else "" 

132 ), 

133 ) 

134 ) 

135 return findings 

136 

137 

138def check_submission_matches_catalog(repo_root: Path, name: str) -> StaticFinding: 

139 """The spec's submission path must agree with the catalog's documented command.""" 

140 meta = _catalog_metadata(repo_root).get(name, {}) 

141 documented = str(meta.get("submission", "")) 

142 spec = EXAMPLE_SPECS[name] 

143 expectations = { 

144 SUBMIT_DIRECT: "gco jobs submit-direct", 

145 SUBMIT_SQS: "gco jobs submit-sqs", 

146 SUBMIT_API: "gco jobs submit ", 

147 DAG_RUN: "gco dag run", 

148 } 

149 if spec.submission == SUBMIT_DIRECT: 

150 # Inference examples document `gco inference deploy` as the 

151 # recommended path and manifest-direct submission as the alternative; 

152 # both are valid documented shapes for a submit-direct spec. 

153 ok = "gco jobs submit-direct" in documented or "gco inference deploy" in documented 

154 detail = "" if ok else f"catalog documents {documented!r}, spec says {spec.submission}" 

155 elif spec.submission in expectations: 

156 ok = expectations[spec.submission] in documented 

157 detail = "" if ok else f"catalog documents {documented!r}, spec says {spec.submission}" 

158 elif spec.submission == COMPANION: 

159 ok = True 

160 detail = "" 

161 else: # kubectl-apply 

162 ok = "kubectl apply" in documented or documented == "" 

163 detail = "" if ok else f"catalog documents {documented!r}, spec says kubectl-apply" 

164 return StaticFinding(example=name, check="documented submission path", passed=ok, detail=detail) 

165 

166 

167def check_transport_acceptance(parsed: ParsedExample) -> list[StaticFinding]: 

168 """API/SQS-documented examples must clear the deployed validation gates.""" 

169 if parsed.spec.submission not in {SUBMIT_DIRECT, SUBMIT_SQS, SUBMIT_API}: 

170 return [] 

171 # Module-level pure functions: no Kubernetes client construction, so the 

172 # checks run on machines with no kubeconfig (CI runners, fresh laptops). 

173 from gco.services.manifest_processor import validate_image_sources, validate_resource_kind 

174 

175 findings: list[StaticFinding] = [] 

176 for doc in parsed.documents: 

177 label = f"{doc.get('kind')}/{(doc.get('metadata') or {}).get('name')}" 

178 if parsed.spec.submission in {SUBMIT_SQS, SUBMIT_API}: 

179 # Only the SQS/API services enforce the kind allowlist; 

180 # submit-direct is client-side kubectl and takes any kind. 

181 kind_ok, kind_reason = validate_resource_kind(doc) 

182 findings.append( 

183 StaticFinding( 

184 example=parsed.name, 

185 check=f"transport kind allowlist ({label})", 

186 passed=kind_ok, 

187 detail=kind_reason or "", 

188 ) 

189 ) 

190 image_ok, image_reason = validate_image_sources(doc) 

191 findings.append( 

192 StaticFinding( 

193 example=parsed.name, 

194 check=f"trusted image sources ({label})", 

195 passed=image_ok, 

196 detail=image_reason or "", 

197 ) 

198 ) 

199 return findings 

200 

201 

202def check_namespaces(parsed: ParsedExample) -> list[StaticFinding]: 

203 """Namespaced example documents must target a provisioned workload namespace.""" 

204 findings: list[StaticFinding] = [] 

205 for doc in parsed.documents: 

206 metadata = doc.get("metadata") or {} 

207 namespace = metadata.get("namespace") 

208 kind = str(doc.get("kind", "")) 

209 if kind in {"ResourceFlavor", "ClusterQueue"}: # cluster-scoped 

210 continue 

211 if namespace is None: 

212 continue 

213 findings.append( 

214 StaticFinding( 

215 example=parsed.name, 

216 check=f"workload namespace ({kind}/{metadata.get('name')})", 

217 passed=namespace in _WORKLOAD_NAMESPACES, 

218 detail="" if namespace in _WORKLOAD_NAMESPACES else f"namespace {namespace!r}", 

219 ) 

220 ) 

221 return findings 

222 

223 

224def check_spec_shape(name: str) -> StaticFinding: 

225 """Spec fields must use known enumerations.""" 

226 spec = EXAMPLE_SPECS[name] 

227 ok = spec.submission in SUBMISSION_PATHS 

228 return StaticFinding( 

229 example=name, 

230 check="spec shape", 

231 passed=ok, 

232 detail="" if ok else f"unknown submission path {spec.submission!r}", 

233 ) 

234 

235 

236#: Container resource dimensions governed by the gco-jobs LimitRange, mapped 

237#: to their per-container default ceiling key in DEFAULT_RESOURCE_QUOTA. 

238_LIMIT_RANGE_DIMENSIONS = { 

239 "cpu": "container_max_cpu", 

240 "memory": "container_max_memory", 

241 "nvidia.com/gpu": "container_max_gpu", 

242} 

243 

244#: Aggregate request dimensions governed by the gco-jobs ResourceQuota. 

245_QUOTA_DIMENSIONS = { 

246 "cpu": "max_cpu", 

247 "memory": "max_memory", 

248 "nvidia.com/gpu": "max_gpu", 

249} 

250 

251 

252def check_resource_governance_fit(parsed: ParsedExample) -> list[StaticFinding]: 

253 """Every example must be admissible under the default gco-jobs governance. 

254 

255 A container exceeding the LimitRange maxima is rejected at pod creation 

256 with only namespace events explaining why, and the Job sits podless until 

257 the caller gives up — the previous defaults rejected the platform's own 

258 EFA training example exactly that way (live run ex241-df723811). Proving 

259 the fit offline keeps the shipped examples and the shipped guardrails 

260 from contradicting each other again. Only gco-jobs-namespaced pod specs 

261 are checked: the LimitRange and ResourceQuota bind that namespace. 

262 """ 

263 from gco.stacks.constants import DEFAULT_RESOURCE_QUOTA, parse_k8s_quantity 

264 

265 findings: list[StaticFinding] = [] 

266 for doc in parsed.documents: 

267 kind = str(doc.get("kind", "")) 

268 metadata = doc.get("metadata") or {} 

269 if metadata.get("namespace", "gco-jobs") != "gco-jobs": 

270 continue 

271 pod_spec, replicas = _pod_spec_and_parallelism(doc, kind) 

272 if pod_spec is None: 

273 continue 

274 containers = list(pod_spec.get("containers") or []) + list( 

275 pod_spec.get("initContainers") or [] 

276 ) 

277 aggregate: dict[str, float] = dict.fromkeys(_QUOTA_DIMENSIONS, 0.0) 

278 for container in containers: 

279 resources = container.get("resources") or {} 

280 requests = resources.get("requests") or {} 

281 limits = resources.get("limits") or {} 

282 for dimension, ceiling_key in _LIMIT_RANGE_DIMENSIONS.items(): 

283 ceiling = parse_k8s_quantity(DEFAULT_RESOURCE_QUOTA[ceiling_key]) 

284 for source_name, source in (("requests", requests), ("limits", limits)): 

285 if dimension not in source: 

286 continue 

287 value = parse_k8s_quantity(source[dimension]) 

288 findings.append( 

289 StaticFinding( 

290 example=parsed.name, 

291 check=( 

292 f"LimitRange fit ({kind}/{metadata.get('name')}: " 

293 f"{container.get('name')} {source_name}.{dimension})" 

294 ), 

295 passed=value <= ceiling, 

296 detail="" 

297 if value <= ceiling 

298 else ( 

299 f"{source[dimension]} exceeds the default per-container " 

300 f"ceiling {DEFAULT_RESOURCE_QUOTA[ceiling_key]} " 

301 f"({ceiling_key})" 

302 ), 

303 ) 

304 ) 

305 for dimension in _QUOTA_DIMENSIONS: 

306 if dimension in requests: 

307 aggregate[dimension] += parse_k8s_quantity(requests[dimension]) 

308 for dimension, quota_key in _QUOTA_DIMENSIONS.items(): 

309 total = aggregate[dimension] * replicas 

310 quota = parse_k8s_quantity(DEFAULT_RESOURCE_QUOTA[quota_key]) 

311 findings.append( 

312 StaticFinding( 

313 example=parsed.name, 

314 check=( 

315 f"ResourceQuota fit ({kind}/{metadata.get('name')}: " 

316 f"{replicas}x pod requests.{dimension})" 

317 ), 

318 passed=total <= quota, 

319 detail="" 

320 if total <= quota 

321 else ( 

322 f"aggregate {total:g} exceeds the default namespace quota " 

323 f"{DEFAULT_RESOURCE_QUOTA[quota_key]} ({quota_key})" 

324 ), 

325 ) 

326 ) 

327 if parsed.spec.submission in {SUBMIT_API, SUBMIT_SQS}: 

328 findings.extend( 

329 _manifest_cap_findings(parsed, kind, str(metadata.get("name")), aggregate, replicas) 

330 ) 

331 return findings 

332 

333 

334#: Front-door budget dimensions (manifest/queue processor caps) by 

335#: DEFAULT_MANIFEST_RESOURCE_CAPS key. 

336_MANIFEST_CAP_DIMENSIONS = { 

337 "cpu": "max_cpu_per_manifest", 

338 "memory": "max_memory_per_manifest", 

339 "nvidia.com/gpu": "max_gpu_per_manifest", 

340} 

341 

342 

343def _manifest_cap_findings( 

344 parsed: ParsedExample, 

345 kind: str, 

346 name: str, 

347 aggregate: dict[str, float], 

348 replicas: int, 

349) -> list[StaticFinding]: 

350 """API/SQS-submitted manifests must also fit the front-door budget. 

351 

352 The manifest and queue processors cap what one submitted manifest may 

353 total; an example the front door rejects while kubectl admits it (or 

354 vice versa) means the layers contradict each other. 

355 """ 

356 from gco.stacks.constants import DEFAULT_MANIFEST_RESOURCE_CAPS, parse_k8s_quantity 

357 

358 findings: list[StaticFinding] = [] 

359 for dimension, cap_key in _MANIFEST_CAP_DIMENSIONS.items(): 

360 total = aggregate[dimension] * replicas 

361 cap = parse_k8s_quantity(DEFAULT_MANIFEST_RESOURCE_CAPS[cap_key]) 

362 findings.append( 

363 StaticFinding( 

364 example=parsed.name, 

365 check=f"manifest-cap fit ({kind}/{name}: {replicas}x pod requests.{dimension})", 

366 passed=total <= cap, 

367 detail="" 

368 if total <= cap 

369 else ( 

370 f"aggregate {total:g} exceeds the default per-manifest cap " 

371 f"{DEFAULT_MANIFEST_RESOURCE_CAPS[cap_key]} ({cap_key})" 

372 ), 

373 ) 

374 ) 

375 return findings 

376 

377 

378def _pod_spec_and_parallelism(doc: dict[str, Any], kind: str) -> tuple[dict[str, Any] | None, int]: 

379 """Extract the pod template spec and concurrent-pod count for a workload.""" 

380 spec = doc.get("spec") or {} 

381 if kind == "Job": 

382 template_spec = ((spec.get("template") or {}).get("spec")) or None 

383 return template_spec, int(spec.get("parallelism", 1) or 1) 

384 if kind in {"Deployment", "StatefulSet"}: 

385 template_spec = ((spec.get("template") or {}).get("spec")) or None 

386 return template_spec, int(spec.get("replicas", 1) or 1) 

387 if kind == "Pod": 

388 return spec or None, 1 

389 if kind == "TrainJob": 

390 # A TrainJob runs its spec.trainer view once per node; the shared 

391 # decomposition builds the same synthetic pod spec the deployed 

392 # validators check, so the offline governance math matches theirs. 

393 from gco.services.manifest_processor import extract_trainjob_pod_specs 

394 

395 trainjob_specs = extract_trainjob_pod_specs(doc) 

396 return trainjob_specs.trainer, trainjob_specs.num_nodes 

397 return None, 1 

398 

399 

400def run_static_checks(repo_root: Path, names: list[str] | None = None) -> list[StaticFinding]: 

401 """Run every offline check; returns findings (all must pass).""" 

402 findings = check_registry_symmetry(repo_root) 

403 for name in names or example_names(repo_root): 

404 findings.append(check_spec_shape(name)) 

405 if EXAMPLE_SPECS.get(name) is None: 

406 continue 

407 parsed = parse_example(repo_root, name) 

408 findings.append(check_submission_matches_catalog(repo_root, name)) 

409 findings.extend(check_transport_acceptance(parsed)) 

410 findings.extend(check_namespaces(parsed)) 

411 findings.extend(check_resource_governance_fit(parsed)) 

412 return findings