sky-lattice/src/skylattice/core/policy.py

77 lines
3.0 KiB
Python
Raw Normal View History

from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
from skylattice.core.decisions import active_decisions, get_decision
from skylattice.core.intent import validate_intent_schema
from skylattice.core.paths import blueprint_dir, load_yaml
@dataclass
class PolicyResult:
errors: list[str] = field(default_factory=list)
warnings: list[str] = field(default_factory=list)
@property
def ok(self) -> bool:
return not self.errors
def run_policies(customer_root, intent: dict[str, Any]) -> PolicyResult:
result = PolicyResult()
result.errors.extend(validate_intent_schema(intent))
rules = load_yaml(blueprint_dir() / "policies" / "rules.yaml").get("rules", [])
checks = {r["check"]: r for r in rules}
if "domains_non_empty" in checks and not intent.get("domains"):
result.errors.append(f"[{checks['domains_non_empty']['id']}] At least one domain is required.")
if "blueprint_pinned" in checks and not intent.get("blueprint"):
result.errors.append(f"[{checks['blueprint_pinned']['id']}] intent.blueprint must be set.")
if "overrides_have_decisions" in checks:
for ov in intent.get("overrides") or []:
did = ov.get("decision_id")
if not did:
result.errors.append(
f"[override_requires_decision] Override {ov.get('path')} missing decision_id."
)
continue
dec = get_decision(customer_root, did)
if not dec:
result.errors.append(
f"[override_requires_decision] Decision '{did}' not found for override {ov.get('path')}."
)
elif dec.get("status") != "active":
result.errors.append(
f"[override_requires_decision] Decision '{did}' is not active."
)
if "unmanaged_have_decisions" in checks:
for um in intent.get("unmanaged") or []:
if not um.get("decision_id"):
result.warnings.append(
f"[unmanaged_requires_decision] Unmanaged {um.get('kind')}:{um.get('name')} has no decision_id."
)
if "restricted_prod_curated_guard" in checks:
restricted = {
d["name"] for d in intent.get("domains") or [] if d.get("access_profile") == "restricted"
}
for ov in intent.get("overrides") or []:
path = ov.get("path") or ""
if "prod_curated_read" in path:
# path like domains.finance.prod_curated_read
parts = path.split(".")
domain = parts[1] if len(parts) > 1 else ""
if domain in restricted and not ov.get("decision_id"):
result.errors.append(
f"[restricted_prod_curated_read] {path} requires a decision."
)
# Ensure referenced active decisions exist for explainability coverage
_ = active_decisions(customer_root)
return result