svcforge: reference implementation
ci / lint (push) Successful in 1m19s
ci / unit (push) Failing after 1m2s
ci / integration (push) Has been skipped
ci / types (push) Successful in 1m37s
ci / security (push) Failing after 38s
ci / dockerfile (push) Successful in 14s
ci / image (api) (push) Has been skipped
ci / image (reconciler) (push) Has been skipped
ci / image (worker) (push) Has been skipped
ci / bump (push) Has been skipped
ci / lint (push) Successful in 1m19s
ci / unit (push) Failing after 1m2s
ci / integration (push) Has been skipped
ci / types (push) Successful in 1m37s
ci / security (push) Failing after 38s
ci / dockerfile (push) Successful in 14s
ci / image (api) (push) Has been skipped
ci / image (reconciler) (push) Has been skipped
ci / image (worker) (push) Has been skipped
ci / bump (push) Has been skipped
Complete working build of the system learn-python/ teaches. 164 tests, mypy --strict clean, domain coverage 99%.
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
"""The real thing: a real helm, against a real cluster, installing a real chart.
|
||||
|
||||
Everything else in this suite runs against a FakeProvisioner, which is what makes the
|
||||
worker tests take milliseconds. That trade has one cost, and this file is the payment: the
|
||||
fake proves the *worker* is correct, and cannot prove the *adapter* is. Argv order, the
|
||||
`--wait` flag, chart resolution, RBAC, whether `upgrade --install` is genuinely idempotent
|
||||
against a live release — none of it is exercised by a fake that returns None.
|
||||
|
||||
So one test does it for real, exactly once. It is marked `e2e` and excluded from `make test`
|
||||
and from every pre-commit run; CI runs `-m e2e` in a job that has a cluster.
|
||||
|
||||
kind create cluster --name svcforge
|
||||
uv run pytest -m e2e -q
|
||||
|
||||
It skips (does not fail) with no cluster, because a laptop without kubectl is not a
|
||||
regression.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import shutil
|
||||
import subprocess
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
from svcforge_core.adapters.helm import HelmProvisioner
|
||||
from svcforge_core.domain.models import CatalogEntry, SizeSpec
|
||||
|
||||
pytestmark = [pytest.mark.e2e, pytest.mark.slow]
|
||||
|
||||
NAMESPACE = "svcforge-e2e"
|
||||
|
||||
# A chart with no dependencies, no PVCs and no image pulls worth waiting on. The point is
|
||||
# to exercise the adapter, not to wait four minutes for Elasticsearch.
|
||||
ENTRY = CatalogEntry(
|
||||
service_type="podinfo",
|
||||
chart="oci://ghcr.io/stefanprodan/charts/podinfo",
|
||||
chart_version="6.7.1",
|
||||
sizes={"small": SizeSpec(replicas=1, resources={})},
|
||||
)
|
||||
|
||||
|
||||
def _cluster_reachable() -> bool:
|
||||
if not shutil.which("helm") or not shutil.which("kubectl"):
|
||||
return False
|
||||
out = subprocess.run(
|
||||
["kubectl", "cluster-info"],
|
||||
capture_output=True,
|
||||
timeout=15,
|
||||
)
|
||||
return out.returncode == 0
|
||||
|
||||
|
||||
requires_cluster = pytest.mark.skipif(
|
||||
not _cluster_reachable(),
|
||||
reason="no reachable cluster: `kind create cluster --name svcforge`",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def namespace() -> str:
|
||||
subprocess.run(
|
||||
["kubectl", "create", "namespace", NAMESPACE],
|
||||
capture_output=True,
|
||||
check=False, # already exists is fine — the whole system is idempotent or it is broken
|
||||
)
|
||||
return NAMESPACE
|
||||
|
||||
|
||||
@requires_cluster
|
||||
async def test_install_is_idempotent_against_a_real_cluster(namespace: str) -> None:
|
||||
"""Install twice. Get one release. This is the claim the fake cannot make for us."""
|
||||
release = f"e2e-podinfo-{uuid.uuid4().hex[:8]}"
|
||||
prov = HelmProvisioner(timeout_s=300)
|
||||
|
||||
try:
|
||||
await prov.install(release=release, ns=namespace, entry=ENTRY, values={"replicaCount": 1})
|
||||
|
||||
releases = [r for r in await prov.list_releases() if r.name == release]
|
||||
assert len(releases) == 1, f"expected exactly one release, got {releases}"
|
||||
|
||||
# The redelivery, for real: same task, same deterministic release name, run again.
|
||||
# `helm upgrade --install` must converge, not duplicate and not error.
|
||||
await prov.install(release=release, ns=namespace, entry=ENTRY, values={"replicaCount": 1})
|
||||
|
||||
releases = [r for r in await prov.list_releases() if r.name == release]
|
||||
assert len(releases) == 1, "second install created a second release: not idempotent"
|
||||
|
||||
# --wait means the DB writing `ready` is telling the truth.
|
||||
out = subprocess.run(
|
||||
[
|
||||
"kubectl",
|
||||
"get",
|
||||
"deploy",
|
||||
"-n",
|
||||
namespace,
|
||||
"-l",
|
||||
f"app.kubernetes.io/instance={release}",
|
||||
"-o",
|
||||
"jsonpath={.items[*].status.readyReplicas}",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30,
|
||||
)
|
||||
assert out.stdout.strip() == "1", f"--wait returned before the pod was ready: {out.stdout!r}"
|
||||
finally:
|
||||
await prov.uninstall(release=release, ns=namespace)
|
||||
|
||||
|
||||
@requires_cluster
|
||||
async def test_uninstall_of_a_missing_release_is_not_an_error(namespace: str) -> None:
|
||||
"""The desired state — no release — is already true. That is success, not failure."""
|
||||
prov = HelmProvisioner(timeout_s=120)
|
||||
await prov.uninstall(release=f"never-existed-{uuid.uuid4().hex[:8]}", ns=namespace)
|
||||
|
||||
|
||||
@requires_cluster
|
||||
async def test_real_helm_timeout_leaves_no_helm_behind(namespace: str) -> None:
|
||||
"""A deadline that does not kill helm is a deadline that lets helm keep mutating."""
|
||||
prov = HelmProvisioner(timeout_s=1) # cannot possibly finish
|
||||
release = f"e2e-timeout-{uuid.uuid4().hex[:8]}"
|
||||
|
||||
with pytest.raises((TimeoutError, Exception)):
|
||||
await prov.install(release=release, ns=namespace, entry=ENTRY, values={})
|
||||
|
||||
await asyncio.sleep(0.5)
|
||||
out = subprocess.run(["pgrep", "-f", f"helm.*{release}"], capture_output=True, text=True)
|
||||
assert out.stdout.strip() == "", "helm survived its own timeout and is still touching the cluster"
|
||||
|
||||
await prov.uninstall(release=release, ns=namespace)
|
||||
Reference in New Issue
Block a user