Coverage for scripts / live_release_validation / checks / eks_capabilities.py: 100.00%
52 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-10-28 16:19 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-10-28 16:19 +0000
1"""Checks behind the ``eks-capabilities`` action.
3The opt-in EKS Capabilities (AWS-managed ACK and kro; see
4``docs/EKS_CAPABILITIES.md``) are off in the shipped ``cdk.json``, and the
5harness's preflight requires a clean worktree, so a run enables them the way
6it enables optional schedulers: through run-scoped CDK context. This module
7turns ``--eks-capabilities`` into the ``eks_capabilities_overrides`` JSON the
8deploy carries and later resolves the same merged block to know what to
9prove.
11What the action proves, per deployed Region: every enabled capability is
12attached to the Region's cluster, ``ACTIVE`` and drift-free — read through
13the same configured-vs-live merge ``gco stacks capabilities status`` uses
14(``cli.eks_capabilities``), so the CLI's drift model and the harness agree.
15What each capability then does in the cluster (a kro ResourceGraphDefinition
16composing a Job, an ACK SQS queue) is the example harness's job
17(``examples/kro-batch-job.yaml``, ``examples/ack-sqs-queue.yaml``).
18"""
20from __future__ import annotations
22import json
23from collections.abc import Mapping
24from typing import Any
26from cli.eks_capabilities import build_status, describe_live_capabilities
27from gco.eks_capabilities_config import (
28 EKS_CAPABILITY_TYPES,
29 EksCapabilitiesConfigError,
30 enabled_capability_types,
31 merge_eks_capabilities_overrides,
32 parse_eks_capabilities_overrides,
33 validate_eks_capabilities_config,
34)
36from ..models import RunContext
39class EksCapabilitiesValidationError(RuntimeError):
40 """The deployed capabilities do not match what the run configured."""
43# ─── run inputs -> eks_capabilities_overrides ────────────────────────────────
46def build_eks_capabilities_overrides(
47 *,
48 types: tuple[str, ...],
49 extra: Mapping[str, Mapping[str, Any]] | None = None,
50) -> dict[str, Any]:
51 """Turn the run's capability selection into the ``eks_capabilities_overrides`` object.
53 Every named type is enabled for every Region the run deploys. ``extra``
54 carries per-type settings merged into that type's block (the example
55 harness uses it to give ACK the managed policy its SQS example needs).
56 Raises ``ValueError`` on an unknown type, or on ``extra`` for a type that
57 is not selected.
58 """
59 unknown = sorted(set(types) - set(EKS_CAPABILITY_TYPES))
60 if unknown:
61 raise ValueError(
62 "unknown EKS capability type(s): "
63 + ", ".join(unknown)
64 + "; valid: "
65 + ", ".join(EKS_CAPABILITY_TYPES)
66 )
67 extra = extra or {}
68 stray = sorted(set(extra) - set(types))
69 if stray:
70 raise ValueError(
71 "capability settings for type(s) the run does not enable: " + ", ".join(stray)
72 )
73 overrides: dict[str, Any] = {}
74 for name in EKS_CAPABILITY_TYPES:
75 if name in types:
76 overrides[name] = {**dict(extra.get(name) or {}), "enabled": True}
77 return overrides
80def overrides_json(overrides: Mapping[str, Any]) -> str:
81 """Canonical JSON for the ``--context`` value and the resume identity."""
82 return json.dumps(overrides, sort_keys=True, separators=(",", ":"))
85# ─── effective configuration ─────────────────────────────────────────────────
88def effective_eks_capabilities_config(ctx: RunContext) -> dict[str, Any]:
89 """The block the run deployed with: cdk.json merged with the run's overrides."""
90 try:
91 overrides = parse_eks_capabilities_overrides(
92 getattr(ctx.settings, "eks_capabilities_overrides_json", "") or None
93 )
94 raw = merge_eks_capabilities_overrides(ctx.cdk_context.get("eks_capabilities"), overrides)
95 return validate_eks_capabilities_config(raw, ctx.deployment_regions)
96 except EksCapabilitiesConfigError as exc:
97 raise EksCapabilitiesValidationError(f"eks_capabilities is invalid: {exc}") from exc
100def enabled_types_by_region(
101 config: Mapping[str, Any], regions: tuple[str, ...]
102) -> dict[str, list[str]]:
103 """``{region: [types]}`` for the Regions where at least one type is on."""
104 result: dict[str, list[str]] = {}
105 for region in regions:
106 enabled = enabled_capability_types(config, region)
107 if enabled:
108 result[region] = enabled
109 return result
112# ─── AWS-side verification ───────────────────────────────────────────────────
115def verify_capabilities_attached(
116 ctx: RunContext,
117 region: str,
118 config: Mapping[str, Any],
119) -> dict[str, Any]:
120 """Require every enabled type ACTIVE on the Region's cluster; return the CLI status document.
122 Uses ``cli.eks_capabilities.build_status`` so a failure here reads exactly
123 like ``gco stacks capabilities status`` would report it.
124 """
125 eks = ctx.session.client("eks", region_name=region)
126 project_name = ctx.config.project_name
127 cluster_name = f"{project_name}-{region}"
128 cluster = eks.describe_cluster(name=cluster_name).get("cluster") or {}
129 cluster_arn = str(cluster.get("arn") or "")
130 if not cluster_arn:
131 raise EksCapabilitiesValidationError(f"{region}: EKS returned no ARN for {cluster_name}")
132 live = describe_live_capabilities(eks, cluster_name)
133 status = build_status(region=region, project_name=project_name, config=config, live=live)
134 problems = [f"{row['type']}: {row['drift']}" for row in status["capabilities"] if row["drift"]]
135 if problems:
136 raise EksCapabilitiesValidationError(
137 f"{region}: capabilities drift from the run configuration: " + "; ".join(problems)
138 )
139 status["cluster_arn"] = cluster_arn
140 return status