Merge pull request 'feat: add zerotier to network cli' (#5178) from Qubasa/clan-core:zerotier_network into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5178
This commit is contained in:
Luis Hebendanz
2025-09-16 19:17:48 +00:00
5 changed files with 92 additions and 3 deletions

2
.gitignore vendored
View File

@@ -52,3 +52,5 @@ pkgs/clan-app/ui/.fonts
*.gif
*.mp4
*.mkv
.jj

View File

@@ -8,8 +8,25 @@
roles.peer = {
perInstance =
{ instanceName, roles, ... }:
{
instanceName,
roles,
lib,
...
}:
{
exports.networking = {
priority = lib.mkDefault 900;
# 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,

View File

@@ -29,10 +29,10 @@ def get_machine_var(machine: Machine, var_id: str) -> Var:
if var.id.startswith(var_id):
results.append(var)
if len(results) == 0:
msg = f"No var found for search string: {var_id}"
msg = f"Couldn't find var: {var_id} for machine: {machine}"
raise ClanError(msg)
if len(results) > 1:
error = f"Found multiple vars for {var_id}:\n - " + "\n - ".join(
error = f"Found multiple vars in {machine} for {var_id}:\n - " + "\n - ".join(
[str(var) for var in results],
)
raise ClanError(error)

View File

@@ -0,0 +1,43 @@
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.network.zerotier.lib import check_zerotier_running
from clan_lib.ssh.remote import Remote
log = logging.getLogger(__name__)
@dataclass(frozen=True)
class NetworkTechnology(NetworkTechnologyBase):
def is_running(self) -> bool:
return check_zerotier_running()
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]:
# TODO: Implement userspace ZeroTier service start/stop
yield network
def remote(self, peer: Peer) -> "Remote":
return Remote(
address=peer.host,
command_prefix=peer.name,
)

View File

@@ -0,0 +1,27 @@
import contextlib
import json
import urllib.request
from typing import TypedDict
class ZeroTierConfig(TypedDict):
clock: int
online: bool
version: str
versionBuild: int
versionMajor: int
versionMinor: int
versionRev: int
def get_zerotier_health() -> ZeroTierConfig:
# Placeholder for actual ZeroTier running check
res = urllib.request.urlopen("http://localhost:9993/health")
return json.load(res)
def check_zerotier_running() -> bool:
with contextlib.suppress(urllib.error.URLError):
get_zerotier_health()
return True
return False