mirror of
https://github.com/sredevopsorg/deepseek-kubernetes-sandbox.git
synced 2026-11-03 01:13:24 +00:00
Fix idle sweeper suspending claims the gateway does not own (#4)
The sweeper listed every SandboxClaim in NAMESPACE and treated each dsh-<user> name as one of its own users. A claim applied by hand from the resource-layer manifests (e.g. 30-claim-alice.yaml) therefore looked exactly like a gateway user: the idle clock started on first sighting and the pod was suspended after IDLE_TIMEOUT. That deletes the pod the README's step 3 logs/port-forward targets, so the walkthrough broke as soon as the gateway was deployed next to it. Mark claims created by create_claim() with aaas.example.com/managed-by=dsh-gateway and skip everything else in sweep_once() before any idle bookkeeping happens. Document the ownership boundary in the example docs, the manifest and ADR-5, add unit coverage for the marker and the skipped sweep, and assert in run-test-kind.sh that alice's hand-applied claim stays Running while bob is correctly idle-suspended. Co-authored-by: SREDevOpsAgents <agents@sredevops.org>
This commit is contained in:
committed by
GitHub
parent
6301c21bea
commit
29eefd6ae5
@@ -29,6 +29,7 @@ from gateway import (
|
||||
derive_state,
|
||||
extract_dsh_cookies,
|
||||
filtered_query,
|
||||
gateway_managed,
|
||||
parse_dsh_launch_token,
|
||||
rewrite_location,
|
||||
same_origin,
|
||||
@@ -318,6 +319,15 @@ def test_claim_name_and_user_roundtrip():
|
||||
assert claim_user({"metadata": {"name": "dsh-alice"}}) == "alice"
|
||||
|
||||
|
||||
def test_gateway_managed_requires_ownership_annotation():
|
||||
managed = {"metadata": {"annotations": {"aaas.example.com/managed-by": "dsh-gateway"}}}
|
||||
manual = {"metadata": {"name": "dsh-alice"}}
|
||||
other = {"metadata": {"annotations": {"aaas.example.com/managed-by": "someone"}}}
|
||||
assert gateway_managed(managed) is True
|
||||
assert gateway_managed(manual) is False
|
||||
assert gateway_managed(other) is False
|
||||
|
||||
|
||||
def test_parse_dsh_launch_token_from_real_log_line():
|
||||
log = (
|
||||
"2026-10-21T10:00:00Z starting\n"
|
||||
@@ -443,6 +453,10 @@ async def test_kubernetes_create_claim_body(monkeypatch):
|
||||
body = args[4]
|
||||
assert body["metadata"]["name"] == "dsh-alice"
|
||||
assert body["metadata"]["annotations"]["aaas.example.com/token-sha256"] == "hash"
|
||||
assert (
|
||||
body["metadata"]["annotations"]["aaas.example.com/managed-by"]
|
||||
== "dsh-gateway"
|
||||
)
|
||||
assert body["spec"]["warmPoolRef"]["name"] == "test-pool"
|
||||
assert body["spec"]["additionalPodMetadata"]["labels"][
|
||||
"sandbox.users.io/dsh-user"
|
||||
@@ -1159,7 +1173,10 @@ def test_ws_upstream_connect_failure_closes_1011(env):
|
||||
|
||||
def ready_claim(user="alice"):
|
||||
return {
|
||||
"metadata": {"name": claim_name(user)},
|
||||
"metadata": {
|
||||
"name": claim_name(user),
|
||||
"annotations": {"aaas.example.com/managed-by": "dsh-gateway"},
|
||||
},
|
||||
"status": {"sandbox": {"name": f"dsh-{user}-sbx"}},
|
||||
}
|
||||
|
||||
@@ -1172,6 +1189,33 @@ def ready_sandbox(user="alice"):
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sweeper_skips_claim_it_does_not_own(env):
|
||||
"""A hand-applied claim (e.g. README 30-claim-alice.yaml) must not be swept.
|
||||
|
||||
The gateway shares the namespace with the resource-layer walkthrough; those
|
||||
claims are named dsh-<user> too, but carry no ownership annotation. The
|
||||
sweeper used to suspend them 60s after first sighting, deleting the pod that
|
||||
README step 3 port-forwards to.
|
||||
"""
|
||||
env.k8s.claims["alice"] = {
|
||||
"metadata": {"name": claim_name("alice")},
|
||||
"status": {"sandbox": {"name": "dsh-alice-sbx"}},
|
||||
}
|
||||
env.k8s.sandboxes["dsh-alice-sbx"] = ready_sandbox()
|
||||
|
||||
# The sweeper never starts an idle clock for a claim it does not own...
|
||||
await env.gateway.sweep_once()
|
||||
assert "alice" not in env.gateway.state.last_activity
|
||||
|
||||
# ...and does not suspend it even when it looks long-idle.
|
||||
env.gateway.state.last_activity["alice"] = (
|
||||
time.time() - env.settings.idle_timeout - 10
|
||||
)
|
||||
await env.gateway.sweep_once()
|
||||
assert env.k8s.patches == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sweeper_suspends_idle_ready_sandbox(env):
|
||||
env.k8s.claims["alice"] = ready_claim()
|
||||
|
||||
Reference in New Issue
Block a user