mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 06:00:33 +02:00
Devices: fixes from review round 18
- A set-up headset whose alias goes through a jump host (ProxyJump or ProxyCommand in ~/.ssh/config) is reached through it, address by address, still pinned per headset. - A refused switch puts the header's switcher back on the headset in use. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
90fd2684ba
commit
c3e3f2629c
3 files changed
+39
-1
No files matched your search
@@ -344,6 +344,15 @@ class Connecting(unittest.TestCase):
|
|||||||
self.assertEqual(self.link.snapshot()["phase"], "connected")
|
self.assertEqual(self.link.snapshot()["phase"], "connected")
|
||||||
self.assertEqual(self.link.named_route(), ("frame-bare", pinned)) # terminals go there too
|
self.assertEqual(self.link.named_route(), ("frame-bare", pinned)) # terminals go there too
|
||||||
|
|
||||||
|
def test_a_set_up_headset_behind_a_jump_host_is_left_to_ssh(self):
|
||||||
|
d = self.device("10.99.99.98", "10.99.99.99") # neither answers directly
|
||||||
|
self.hosts({"10.99.99.98": "wrong", "10.99.99.99": "ok"})
|
||||||
|
with mock.patch.object(fl, "ssh_g", return_value=("frame-t", 22, "steamos", True)):
|
||||||
|
self.link.connect(["start"])
|
||||||
|
s = self.link.snapshot()
|
||||||
|
self.assertEqual((s["phase"], s["via"]["host"]), ("connected", "10.99.99.99"), s["error"])
|
||||||
|
self.assertIn(f"HostKeyAlias=frame-control-{d['id']}", self.routes[-1][1]) # still pinned per headset
|
||||||
|
|
||||||
def test_a_bare_alias_behind_a_jump_host_is_left_to_ssh(self):
|
def test_a_bare_alias_behind_a_jump_host_is_left_to_ssh(self):
|
||||||
self.link.override = "frame-jump"
|
self.link.override = "frame-jump"
|
||||||
self.hosts({"10.99.99.99": "ok"}) # ssh's ProxyJump would get there
|
self.hosts({"10.99.99.99": "ok"}) # ssh's ProxyJump would get there
|
||||||
|
|||||||
@@ -584,6 +584,31 @@ class Link:
|
|||||||
ranked = [(a, "from ~/.ssh/config")]
|
ranked = [(a, "from ~/.ssh/config")]
|
||||||
else:
|
else:
|
||||||
ranked = frame_devices.order_addresses(device["addresses"], net.get("id"), bool(ts.get("up")))
|
ranked = frame_devices.order_addresses(device["addresses"], net.get("id"), bool(ts.get("up")))
|
||||||
|
if ssh_g(device["alias"])[3]:
|
||||||
|
# ~/.ssh/config sends this alias through a jump host: a direct probe says
|
||||||
|
# nothing, so let ssh (through the jump host) try each address in turn.
|
||||||
|
with self.cond:
|
||||||
|
self.state["probes"] = [dict(a, why=why, state="waiting", detail="Through a jump host", ip=None,
|
||||||
|
rtt_ms=None, label=a.get("label") or "") for a, why in ranked]
|
||||||
|
self.stage("find", "done", f"{device['alias']} goes through a jump host; ssh finds it")
|
||||||
|
for i, (a, why) in enumerate(ranked):
|
||||||
|
if i:
|
||||||
|
for sid in ("ssh", "identity", "login"):
|
||||||
|
self.stage(sid, "pending", "")
|
||||||
|
outcome = self.handshake(device, a, {"ip": None, "rtt_ms": None}, device.get("user"))
|
||||||
|
if outcome == "ok":
|
||||||
|
self.probe_update(i, state="answered", detail="Reached through the jump host")
|
||||||
|
self.publish(via={"host": a["host"], "kind": a["kind"], "ip": None, "rtt_ms": None,
|
||||||
|
"why": "through a jump host", "network": net.get("id"),
|
||||||
|
"network_name": net["name"]})
|
||||||
|
self.learn(device, a["host"], net, None)
|
||||||
|
return True
|
||||||
|
with self.cond:
|
||||||
|
why_not = (self.state["error"] or {}).get("message") or "SSH failed"
|
||||||
|
self.probe_update(i, state="sshfailed", detail=why_not)
|
||||||
|
if outcome != "next":
|
||||||
|
return False
|
||||||
|
return False
|
||||||
with self.cond:
|
with self.cond:
|
||||||
self.state["probes"] = [{"host": a["host"], "kind": a["kind"], "label": a.get("label") or "",
|
self.state["probes"] = [{"host": a["host"], "kind": a["kind"], "label": a.get("label") or "",
|
||||||
"why": why, "state": "waiting", "detail": "Waiting", "ip": None,
|
"why": why, "state": "waiting", "detail": "Waiting", "ip": None,
|
||||||
|
|||||||
+5
-1
@@ -2677,7 +2677,11 @@ async function useDevice(id) {
|
|||||||
const d = dv.list.find(x => x.id === id);
|
const d = dv.list.find(x => x.id === id);
|
||||||
if (!d || d.active) return;
|
if (!d || d.active) return;
|
||||||
devGen++; // from here on, answers about the last headset are dropped
|
devGen++; // from here on, answers about the last headset are dropped
|
||||||
await act(`Switch to ${d.name}`, () => api("/api/devices", { action: "use", id }));
|
const res = await act(`Switch to ${d.name}`, () => api("/api/devices", { action: "use", id }));
|
||||||
|
if (!res) { // refused (an install is running): show the headset still in use
|
||||||
|
const current = dv.list.find(x => x.active);
|
||||||
|
if (current) $("devSel").value = current.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async function devAction(label, body, btn) {
|
async function devAction(label, body, btn) {
|
||||||
const res = await act(label, () => api("/api/devices", body), btn);
|
const res = await act(label, () => api("/api/devices", body), btn);
|
||||||
|
|||||||
Reference in new issue
Block a user