Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
212d5de
docs: add verification schema v1 design spec
geojaz Jul 20, 2026
66844d5
fix(spec): correct verification-schema-v1 Component 1/3 wiring
geojaz Jul 20, 2026
4747e6e
feat(tasks): add typed VerificationEntry and Task.verification_entries
geojaz Jul 21, 2026
7ec39b1
feat(verification): add all/any/none combinators and quantifier dispatch
geojaz Jul 21, 2026
fd08a70
feat(k8s): add run_pod one-shot ephemeral pod helper
geojaz Jul 21, 2026
9566950
feat(verification): add resource_property verifier with JSONPath (jso…
geojaz Jul 21, 2026
139e4c1
feat(verification): add http_probe verifier over one-shot curl pod
geojaz Jul 21, 2026
32044ae
feat(verification): add entry-level c/rec_v/cat_v rollup
geojaz Jul 21, 2026
1e89439
feat(verification): per-entry mode dispatch (converge/assert/hold) vi…
geojaz Jul 21, 2026
1b037bb
feat(evalharness): evaluate verification_entries and emit c/rec_v/cat…
geojaz Jul 21, 2026
369cfca
feat(tasks): onboard deploy-hello-app to objective/safeguard verifica…
geojaz Jul 21, 2026
18f884b
docs(tasks): oracle manifest + validation runbook for deploy-hello-app
geojaz Jul 21, 2026
765d6f2
docs: link deploy-hello-app oracle validation runbook from docs index
geojaz Jul 21, 2026
1f8bbf1
docs(verification): refresh runner + package docstrings for modes/com…
geojaz Jul 21, 2026
21ec224
feat(tasks): add HTTP-server oracle image for deploy-hello-app
geojaz Jul 21, 2026
a3edded
fix(verification): poll http_probe until success or deadline
geojaz Jul 21, 2026
c5d250c
refactor(tasks): defer deploy-hello-app internet-exposure to the judge
geojaz Jul 21, 2026
35fb3fa
fix(verification): make http_probe status parse robust to kubectl noise
geojaz Jul 21, 2026
a278ab8
feat(verification): add external_http_probe verifier
geojaz Jul 21, 2026
047e296
feat(tasks): grade deploy-hello-app exposure via off-cluster probe
geojaz Jul 21, 2026
9187cda
fix(verification): namespace-scoped discovery for external_http_probe
geojaz Jul 22, 2026
9cce758
feat(cli): print deterministic + judge run summary
geojaz Jul 22, 2026
559fae3
fix(verification): quantity-aware resource_property comparisons
geojaz Jul 22, 2026
7517dff
feat(verification): add git_repo_sync verifier
geojaz Jul 22, 2026
71ce3e4
feat(scripts): isolated-run helper and per-task cleanup hooks
geojaz Jul 22, 2026
a654c73
feat(tasks): onboard eight tasks to deterministic verification_entries
geojaz Jul 22, 2026
6e6eaba
fix(scripts): honor preset model and timeout env vars in isorun
geojaz Jul 23, 2026
0d8d425
fix(tf): correct hypercomputer-d1 cluster outputs
geojaz Jul 23, 2026
6b05026
feat(isorun): seed and preflight loop for fast task iteration
geojaz Jul 23, 2026
de87f77
docs(specs): add ledger-facade + checkout-outage task designs
geojaz Jul 24, 2026
f3db07b
docs(plans): add ledger-facade + checkout-outage implementation plan
geojaz Jul 24, 2026
b205d37
feat(tf): add reusable ingress-nginx module
geojaz Jul 24, 2026
93aef1d
feat(tf): install ingress-nginx in prebuilt/kind behind a gate
geojaz Jul 24, 2026
16d1b60
feat(tasks): add ledger-read-facade task (id 23) with isorun hooks
geojaz Jul 24, 2026
7f7125f
feat(tasks): add checkout-multi-service-outage task (id 24) with isor…
geojaz Jul 24, 2026
7f2b866
fix(tasks): correct fixture/hook issues found during isorun validation
geojaz Jul 24, 2026
166a7b6
docs: sync docs for ledger/checkout tasks and ingress-nginx module
geojaz Jul 24, 2026
96c6d80
fix(tasks): address task-review findings on checkout-multi-service-ou…
geojaz Jul 24, 2026
b5a7381
fix: address final-review findings
geojaz Jul 24, 2026
e6e2c64
Delete docs/appendix/deploy-hello-app-oracle-validation.md
geojaz Jul 24, 2026
aa95a8c
feat(isorun): provision kind tasks already-broken; default to standar…
geojaz Jul 24, 2026
1a64235
docs: sync isorun README and infra.md for provisioning-default kind t…
geojaz Jul 24, 2026
2a50c18
style: apply ruff format
geojaz Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions deployers/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from abc import ABC, abstractmethod
from typing import Dict, Any


class Deployer(ABC):
"""
Abstract base class for cloud-specific infra deployers.
Expand Down
10 changes: 5 additions & 5 deletions deployers/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ def __init__(self, cluster_name: str, project_id: str):
self._project_id = project_id

def up(self) -> None:
print("[NoOpDeployer] Skipping infrastructure provisioning (deployer: noop / BENCH_NO_INFRA)")
print(
"[NoOpDeployer] Skipping infrastructure provisioning (deployer: noop / BENCH_NO_INFRA)"
)

def down(self) -> None:
print("[NoOpDeployer] Skipping infrastructure teardown (deployer: noop / BENCH_NO_INFRA)")
Expand All @@ -39,7 +41,7 @@ def get_deployer(
infra_config: Dict[str, Any],
global_project_id: str,
global_cluster_name: str,
global_location: str = None
global_location: str = None,
) -> Deployer:
"""
Factory to instantiate the appropriate infrastructure deployer.
Expand Down Expand Up @@ -82,7 +84,5 @@ def get_deployer(

# Fallback to legacy GCPDeployer (kubetest2)
return GCPDeployer(
project=global_project_id,
location=location,
cluster_name=global_cluster_name
project=global_project_id, location=location, cluster_name=global_cluster_name
)
92 changes: 58 additions & 34 deletions deployers/gcp/gcp_deployer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,36 @@
from typing import Dict, Any
from deployers.base import Deployer


class GCPDeployer(Deployer):
"""
GCP implementation of the Deployer interface using kubetest2 gke.

Migrated to pathlib for better cross-platform compatibility and readability.
Supports both 'location' and 'zone' in initialization for robust API compatibility.
"""
def __init__(self, project: str, location: str = None, cluster_name: str = None, zone: str = None, **config):

def __init__(
self,
project: str,
location: str = None,
cluster_name: str = None,
zone: str = None,
**config,
):
self.project = project
self.cluster_name = cluster_name
self.config = config

# Robust location resolution for backward compatibility
self.location = location or zone or os.environ.get("GCP_LOCATION", "us-central1-a")
self.zone = self.location
self.zone = self.location

# This file is at deployers/gcp/gcp_deployer.py
# Project root is 2 levels up.
current_dir = Path(__file__).resolve().parent
self.bin_dir = str((current_dir.parents[1] / "third_party" / "kubetest2" / "bin").resolve())

def _get_env(self) -> Dict[str, str]:
env = os.environ.copy()
# Ensure kubetest2 and its plugins are in PATH
Expand All @@ -44,8 +53,15 @@ def _state_marker_path(self) -> Path:
def up(self) -> None:
# Check if cluster exists
check_cmd = [
"gcloud", "container", "clusters", "describe", self.cluster_name,
"--project", self.project, "--location", self.location
"gcloud",
"container",
"clusters",
"describe",
self.cluster_name,
"--project",
self.project,
"--location",
self.location,
]
print(f"Checking if cluster exists: {' '.join(check_cmd)}")
result = subprocess.run(check_cmd, capture_output=True, text=True)
Expand All @@ -56,28 +72,36 @@ def up(self) -> None:
if result.returncode == 0:
print(f"Cluster {self.cluster_name} already exists. Getting credentials.")
get_cred_cmd = [
"gcloud", "container", "clusters", "get-credentials", self.cluster_name,
"--project", self.project, "--location", self.location
"gcloud",
"container",
"clusters",
"get-credentials",
self.cluster_name,
"--project",
self.project,
"--location",
self.location,
]
subprocess.run(get_cred_cmd, check=True)
# Record that we didn't create it
state_file.write_text("false")
else:
print(
f"Cluster {self.cluster_name} does not exist or error checking. "
"Creating it."
)
print(f"Cluster {self.cluster_name} does not exist or error checking. Creating it.")
cmd = [
"kubetest2", "gke",
"--project", self.project,
"--zone", self.location, # Passing resolved location to --zone flag in kubetest2
"--cluster-name", self.cluster_name,
"kubetest2",
"gke",
"--project",
self.project,
"--zone",
self.location, # Passing resolved location to --zone flag in kubetest2
"--cluster-name",
self.cluster_name,
]
for key, value in self.config.items():
if value is not None:
flag_name = f"--{key.replace('_', '-')}"
cmd.extend([flag_name, str(value)])

cmd.append("--up")
print(f"Running: {' '.join(cmd)}")
subprocess.run(cmd, env=self._get_env(), check=True)
Expand All @@ -89,31 +113,31 @@ def down(self) -> None:
created_by_us = True
if state_file.exists():
created_by_us = state_file.read_text().strip() == "true"

if not created_by_us:
print(
f"Skipping teardown for pre-existing cluster {self.cluster_name}"
)
print(f"Skipping teardown for pre-existing cluster {self.cluster_name}")
return

cmd = [
"kubetest2", "gke",
"--project", self.project,
"--zone", self.location,
"--cluster-name", self.cluster_name,
"--down"
"kubetest2",
"gke",
"--project",
self.project,
"--zone",
self.location,
"--cluster-name",
self.cluster_name,
"--down",
]
print(f"Running: {' '.join(cmd)}")
subprocess.run(cmd, env=self._get_env(), check=True)

def get_cluster_info(self) -> Dict[str, Any]:
kubeconfig_path = os.environ.get(
"KUBECONFIG", str(Path.home() / ".kube" / "config")
)
kubeconfig_path = os.environ.get("KUBECONFIG", str(Path.home() / ".kube" / "config"))
return {
"name": self.cluster_name,
"location": self.location,
"zone": self.location,
"zone": self.location,
"project": self.project,
"kubeconfig_path": kubeconfig_path
"kubeconfig_path": kubeconfig_path,
}
3 changes: 2 additions & 1 deletion deployers/gcp/variables.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import os
from typing import Any, Dict


def resolve_variables(
stack: str,
custom_variables: Dict[str, Any],
global_project_id: str,
global_cluster_name: str,
global_location: str
global_location: str,
) -> Dict[str, Any]:
"""Resolves default variables for GCP-based TF stacks."""
variables = custom_variables.copy()
Expand Down
3 changes: 2 additions & 1 deletion deployers/kind/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import pathlib
from typing import Any, Dict


def resolve_variables(
stack: str,
custom_variables: Dict[str, Any],
global_project_id: str,
global_cluster_name: str,
global_location: str
global_location: str,
) -> Dict[str, Any]:
"""Resolves default variables for local KinD-based stacks."""
variables = custom_variables.copy()
Expand Down
59 changes: 28 additions & 31 deletions deployers/tf/tf_deployer.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class TFDeployer(Deployer):
Supports the standard TF_DATA_DIR environment variable for controlling
where OpenTofu stores its state, enabling idempotent runs.
"""

def __init__(self, tf_dir: str, variables: Dict[str, Any] = None):
# Locate project root (3 levels up from this file)
repo_root = Path(__file__).resolve().parents[2]
Expand All @@ -59,10 +60,7 @@ def __init__(self, tf_dir: str, variables: Dict[str, Any] = None):
if repo_tf_path.exists():
self.tf_dir = str(repo_tf_path)
else:
raise ValueError(
f"TF stack not found in repo: {tf_dir} "
f"(checked {repo_tf_path})"
)
raise ValueError(f"TF stack not found in repo: {tf_dir} (checked {repo_tf_path})")

# Per-run private working directory (a copy of the tf/ tree under the
# run's scratch dir) when isolated; otherwise the shared stack dir.
Expand All @@ -87,18 +85,14 @@ def _state_flags() -> list:
return []
return ["-state", str(Path(tf_data_dir).resolve().parent / "terraform.tfstate")]

def _run_cmd(
self, cmd: list, cwd: str, capture: bool = False
) -> subprocess.CompletedProcess:
def _run_cmd(self, cmd: list, cwd: str, capture: bool = False) -> subprocess.CompletedProcess:
print(f"Executing: {' '.join(cmd)} in {cwd}")
env = os.environ.copy()
if "TF_DATA_DIR" in env:
print(f"Using TF_DATA_DIR: {env['TF_DATA_DIR']}")
print(f"Using TF_DATA_DIR: {env['TF_DATA_DIR']}")

if capture:
return subprocess.run(
cmd, cwd=cwd, check=True, capture_output=True, text=True, env=env
)
return subprocess.run(cmd, cwd=cwd, check=True, capture_output=True, text=True, env=env)
else:
return subprocess.run(cmd, cwd=cwd, check=True, env=env)

Expand All @@ -115,14 +109,10 @@ def up(self) -> None:

self._run_cmd(cmd, cwd=self.work_dir)


def down(self) -> None:
tf_path = Path(self.work_dir)
if not tf_path.exists():
print(
f"Warning: TF directory {self.work_dir} not found. "
"Skipping teardown."
)
print(f"Warning: TF directory {self.work_dir} not found. Skipping teardown.")
return

self._run_cmd(["tofu", "init", "-input=false"], cwd=self.work_dir)
Expand All @@ -149,37 +139,44 @@ def get_cluster_info(self) -> Dict[str, Any]:

location = outputs.get("cluster_location", {}).get("value")
if not location:
raise ValueError(
"Failed to retrieve 'cluster_location' from TF outputs."
)
raise ValueError("Failed to retrieve 'cluster_location' from TF outputs.")

kubeconfig_path = os.environ.get(
"KUBECONFIG", str(Path.home() / ".kube" / "config")
)
kubeconfig_path = os.environ.get("KUBECONFIG", str(Path.home() / ".kube" / "config"))

if location == "local":
project = self.variables.get("project_id") or os.environ.get("GCP_PROJECT_ID") or "local-kind"
project = (
self.variables.get("project_id") or os.environ.get("GCP_PROJECT_ID") or "local-kind"
)
return {
"name": cluster_name,
"location": location,
"project": project,
"kubeconfig_path": kubeconfig_path
"kubeconfig_path": kubeconfig_path,
}

project = self.variables.get("project_id") or os.environ.get("GCP_PROJECT_ID")
if not project:
raise ValueError("Project ID not found in variables or environment (GCP_PROJECT_ID).")
raise ValueError("Project ID not found in variables or environment (GCP_PROJECT_ID).")

print(f"Configuring kubectl for cluster: {cluster_name} in {location}...")
subprocess.run([
"gcloud", "container", "clusters", "get-credentials", cluster_name,
"--location", location, "--project", project
], check=True)
subprocess.run(
[
"gcloud",
"container",
"clusters",
"get-credentials",
cluster_name,
"--location",
location,
"--project",
project,
],
check=True,
)

return {
"name": cluster_name,
"location": location,
"project": project,
"kubeconfig_path": kubeconfig_path
"kubeconfig_path": kubeconfig_path,
}

Loading
Loading