From 3d8fab062d89fd1ab3141a77734ce12688a6201f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20B=C3=B6hme?= Date: Mon, 1 Sep 2025 12:43:40 +0200 Subject: [PATCH] feat: add zerotier to network cli --- .gitignore | 2 + clanServices/zerotier/default.nix | 19 ++++++++- .../clan_lib/network/zerotier/__init__.py | 41 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 pkgs/clan-cli/clan_lib/network/zerotier/__init__.py diff --git a/.gitignore b/.gitignore index 75dbd7889..ba5b4a4a2 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,5 @@ pkgs/clan-app/ui/.fonts *.gif *.mp4 *.mkv + +.jj diff --git a/clanServices/zerotier/default.nix b/clanServices/zerotier/default.nix index 04e396b89..b689e0540 100644 --- a/clanServices/zerotier/default.nix +++ b/clanServices/zerotier/default.nix @@ -8,8 +8,25 @@ roles.peer = { perInstance = - { instanceName, roles, ... }: { + instanceName, + roles, + lib, + ... + }: + { + exports.networking = { + priority = lib.mkDefault 20; + # TODO add user space network support to clan-cli + module = "clan_lib.network.zerotier"; + peers = lib.mapAttrs (name: machine: { + host.var = { + machine = name; + generator = "zerotier"; + file = "zerotier-ip"; + }; + }) roles.peer.machines; + }; nixosModule = { config, diff --git a/pkgs/clan-cli/clan_lib/network/zerotier/__init__.py b/pkgs/clan-cli/clan_lib/network/zerotier/__init__.py new file mode 100644 index 000000000..33034e5ec --- /dev/null +++ b/pkgs/clan-cli/clan_lib/network/zerotier/__init__.py @@ -0,0 +1,41 @@ +import logging +import time +from collections.abc import Iterator +from contextlib import contextmanager +from dataclasses import dataclass + +from clan_lib.errors import ClanError +from clan_lib.network import Network, NetworkTechnologyBase, Peer +from clan_lib.ssh.remote import Remote + +log = logging.getLogger(__name__) + + +@dataclass(frozen=True) +class NetworkTechnology(NetworkTechnologyBase): + def is_running(self) -> bool: + return True + + def ping(self, remote: Remote) -> None | float: + if self.is_running(): + try: + # Use the existing SSH reachability check + now = time.time() + remote.check_machine_ssh_reachable() + + return (time.time() - now) * 1000 + + except ClanError as e: + log.debug(f"Error checking peer {remote}: {e}") + return None + return None + + @contextmanager + def connection(self, network: Network) -> Iterator[Network]: + yield network + + def remote(self, peer: Peer) -> "Remote": + return Remote( + address=peer.host, + command_prefix=peer.name, + )