Merge pull request 'api/tasks: prefix impure actions with run' (#4239) from api-cleanup into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4239
This commit is contained in:
@@ -75,7 +75,7 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setInstalling(true);
|
setInstalling(true);
|
||||||
await callApi("install_machine", {
|
await callApi("run_machine_install", {
|
||||||
opts: {
|
opts: {
|
||||||
machine: {
|
machine: {
|
||||||
name: name,
|
name: name,
|
||||||
@@ -163,7 +163,7 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await callApi(
|
await callApi(
|
||||||
"deploy_machine",
|
"run_machine_deploy",
|
||||||
{
|
{
|
||||||
machine: {
|
machine: {
|
||||||
name: name,
|
name: name,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const EditClanForm = (props: EditClanFormProps) => {
|
|||||||
const handleSubmit: SubmitHandler<GeneralData> = async (values, event) => {
|
const handleSubmit: SubmitHandler<GeneralData> = async (values, event) => {
|
||||||
await toast.promise(
|
await toast.promise(
|
||||||
(async () => {
|
(async () => {
|
||||||
await callApi("update_clan_meta", {
|
await callApi("set_clan_details", {
|
||||||
options: {
|
options: {
|
||||||
flake: { identifier: props.directory },
|
flake: { identifier: props.directory },
|
||||||
meta: values,
|
meta: values,
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ export const Flash = () => {
|
|||||||
console.log("Confirmed flash:", values);
|
console.log("Confirmed flash:", values);
|
||||||
try {
|
try {
|
||||||
await toast.promise(
|
await toast.promise(
|
||||||
callApi("flash_machine", {
|
callApi("run_machine_flash", {
|
||||||
machine: {
|
machine: {
|
||||||
name: values.machine.devicePath,
|
name: values.machine.devicePath,
|
||||||
flake: {
|
flake: {
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export function InstallMachine(props: InstallMachineProps) {
|
|||||||
throw new Error("No target host found for the machine");
|
throw new Error("No target host found for the machine");
|
||||||
}
|
}
|
||||||
|
|
||||||
const installPromise = callApi("install_machine", {
|
const installPromise = callApi("run_machine_install", {
|
||||||
opts: {
|
opts: {
|
||||||
machine: {
|
machine: {
|
||||||
name: props.name,
|
name: props.name,
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export function MachineForm(props: MachineFormProps) {
|
|||||||
|
|
||||||
setIsUpdating(true);
|
setIsUpdating(true);
|
||||||
const r = await callApi(
|
const r = await callApi(
|
||||||
"deploy_machine",
|
"run_machine_deploy",
|
||||||
{
|
{
|
||||||
machine: {
|
machine: {
|
||||||
name: machine,
|
name: machine,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const EditClanForm = (props: EditClanFormProps) => {
|
|||||||
const handleSubmit: SubmitHandler<GeneralData> = async (values, event) => {
|
const handleSubmit: SubmitHandler<GeneralData> = async (values, event) => {
|
||||||
await toast.promise(
|
await toast.promise(
|
||||||
(async () => {
|
(async () => {
|
||||||
await callApi("update_clan_meta", {
|
await callApi("set_clan_details", {
|
||||||
options: {
|
options: {
|
||||||
flake: { identifier: props.directory },
|
flake: { identifier: props.directory },
|
||||||
meta: values,
|
meta: values,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class Disk:
|
|||||||
|
|
||||||
# TODO: unify this with machine install
|
# TODO: unify this with machine install
|
||||||
@API.register
|
@API.register
|
||||||
def flash_machine(
|
def run_machine_flash(
|
||||||
machine: Machine,
|
machine: Machine,
|
||||||
*,
|
*,
|
||||||
mode: str,
|
mode: str,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from clan_lib.machines.machines import Machine
|
|||||||
|
|
||||||
from clan_cli.completions import add_dynamic_completer, complete_machines
|
from clan_cli.completions import add_dynamic_completer, complete_machines
|
||||||
|
|
||||||
from .flash import Disk, SystemConfig, flash_machine
|
from .flash import Disk, SystemConfig, run_machine_flash
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ def flash_command(args: argparse.Namespace) -> None:
|
|||||||
if ask != "y":
|
if ask != "y":
|
||||||
return
|
return
|
||||||
|
|
||||||
flash_machine(
|
run_machine_flash(
|
||||||
machine,
|
machine,
|
||||||
mode=opts.mode,
|
mode=opts.mode,
|
||||||
disks=opts.disks,
|
disks=opts.disks,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from clan_lib.errors import ClanError
|
from clan_lib.errors import ClanError
|
||||||
from clan_lib.machines.install import BuildOn, InstallOptions, install_machine
|
from clan_lib.machines.install import BuildOn, InstallOptions, run_machine_install
|
||||||
from clan_lib.machines.machines import Machine
|
from clan_lib.machines.machines import Machine
|
||||||
from clan_lib.ssh.remote import Remote
|
from clan_lib.ssh.remote import Remote
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ def install_command(args: argparse.Namespace) -> None:
|
|||||||
if ask != "y":
|
if ask != "y":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return install_machine(
|
return run_machine_install(
|
||||||
InstallOptions(
|
InstallOptions(
|
||||||
machine=machine,
|
machine=machine,
|
||||||
kexec=args.kexec,
|
kexec=args.kexec,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from clan_lib.machines.actions import list_machines
|
|||||||
from clan_lib.machines.list import instantiate_inventory_to_machines
|
from clan_lib.machines.list import instantiate_inventory_to_machines
|
||||||
from clan_lib.machines.machines import Machine
|
from clan_lib.machines.machines import Machine
|
||||||
from clan_lib.machines.suggestions import validate_machine_names
|
from clan_lib.machines.suggestions import validate_machine_names
|
||||||
from clan_lib.machines.update import deploy_machine
|
from clan_lib.machines.update import run_machine_deploy
|
||||||
from clan_lib.nix import nix_config
|
from clan_lib.nix import nix_config
|
||||||
from clan_lib.ssh.remote import Remote
|
from clan_lib.ssh.remote import Remote
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ def update_command(args: argparse.Namespace) -> None:
|
|||||||
tid=machine.name,
|
tid=machine.name,
|
||||||
async_ctx=AsyncContext(prefix=machine.name),
|
async_ctx=AsyncContext(prefix=machine.name),
|
||||||
),
|
),
|
||||||
deploy_machine,
|
run_machine_deploy,
|
||||||
machine=machine,
|
machine=machine,
|
||||||
target_host=target_host,
|
target_host=target_host,
|
||||||
build_host=machine.build_host(),
|
build_host=machine.build_host(),
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ from pathlib import Path
|
|||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from typing import IO, Any
|
from typing import IO, Any
|
||||||
|
|
||||||
from clan_lib.api import API
|
|
||||||
from clan_lib.cmd import Log, RunOpts, run
|
from clan_lib.cmd import Log, RunOpts, run
|
||||||
from clan_lib.dirs import user_config_dir
|
from clan_lib.dirs import user_config_dir
|
||||||
from clan_lib.errors import ClanError
|
from clan_lib.errors import ClanError
|
||||||
@@ -398,7 +397,6 @@ def default_admin_private_key_path() -> Path:
|
|||||||
return user_config_dir() / "sops" / "age" / "keys.txt"
|
return user_config_dir() / "sops" / "age" / "keys.txt"
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
|
||||||
def maybe_get_admin_public_keys() -> list[SopsKey] | None:
|
def maybe_get_admin_public_keys() -> list[SopsKey] | None:
|
||||||
keyring = SopsKey.collect_public_keys()
|
keyring = SopsKey.collect_public_keys()
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ def find_reachable_host(deploy_info: DeployInfo) -> Remote | None:
|
|||||||
return deploy_info.addrs[0]
|
return deploy_info.addrs[0]
|
||||||
|
|
||||||
for addr in deploy_info.addrs:
|
for addr in deploy_info.addrs:
|
||||||
if addr.is_ssh_reachable():
|
if addr.check_machine_ssh_reachable():
|
||||||
return addr
|
return addr
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from clan_lib.errors import ClanError
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Unify with "create clan" should be done automatically
|
||||||
@API.register
|
@API.register
|
||||||
def create_secrets_user(
|
def create_secrets_user(
|
||||||
flake_dir: Path, user: str | None = None, force: bool = False
|
flake_dir: Path, user: str | None = None, force: bool = False
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class UpdateOptions:
|
|||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def update_clan_meta(options: UpdateOptions) -> InventorySnapshot:
|
def set_clan_details(options: UpdateOptions) -> InventorySnapshot:
|
||||||
inventory_store = InventoryStore(options.flake)
|
inventory_store = InventoryStore(options.flake)
|
||||||
inventory = inventory_store.read()
|
inventory = inventory_store.read()
|
||||||
set_value_by_path(inventory, "meta", options.meta)
|
set_value_by_path(inventory, "meta", options.meta)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def example_function() -> None:
|
|||||||
"""Example function for creating logs."""
|
"""Example function for creating logs."""
|
||||||
|
|
||||||
|
|
||||||
def deploy_machine() -> None:
|
def run_machine_deploy() -> None:
|
||||||
"""Function for deploying machines."""
|
"""Function for deploying machines."""
|
||||||
|
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ def main() -> None:
|
|||||||
for repo in repos:
|
for repo in repos:
|
||||||
for machine in machines:
|
for machine in machines:
|
||||||
log_manager.create_log_file(
|
log_manager.create_log_file(
|
||||||
deploy_machine,
|
run_machine_deploy,
|
||||||
f"deploy_{machine}",
|
f"deploy_{machine}",
|
||||||
["clans", repo, "machines", machine],
|
["clans", repo, "machines", machine],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from clan_lib.log_manager import (
|
|||||||
|
|
||||||
|
|
||||||
# Test functions for log creation
|
# Test functions for log creation
|
||||||
def deploy_machine() -> None:
|
def run_machine_deploy() -> None:
|
||||||
"""Test function for deploying machines."""
|
"""Test function for deploying machines."""
|
||||||
|
|
||||||
|
|
||||||
@@ -194,13 +194,13 @@ class TestLogFileCreation:
|
|||||||
for repo in repos:
|
for repo in repos:
|
||||||
for machine in machines:
|
for machine in machines:
|
||||||
log_file = configured_log_manager.create_log_file(
|
log_file = configured_log_manager.create_log_file(
|
||||||
deploy_machine,
|
run_machine_deploy,
|
||||||
f"deploy_{machine}",
|
f"deploy_{machine}",
|
||||||
["clans", repo, "machines", machine],
|
["clans", repo, "machines", machine],
|
||||||
)
|
)
|
||||||
|
|
||||||
assert log_file.op_key == f"deploy_{machine}"
|
assert log_file.op_key == f"deploy_{machine}"
|
||||||
assert log_file.func_name == "deploy_machine"
|
assert log_file.func_name == "run_machine_deploy"
|
||||||
assert log_file.get_file_path().exists()
|
assert log_file.get_file_path().exists()
|
||||||
|
|
||||||
# Check the group structure includes URL encoding for dynamic parts
|
# Check the group structure includes URL encoding for dynamic parts
|
||||||
@@ -241,7 +241,7 @@ class TestFilterFunction:
|
|||||||
"""Test that empty filter returns top-level groups."""
|
"""Test that empty filter returns top-level groups."""
|
||||||
# Create some log files first
|
# Create some log files first
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine, "test_op", ["clans", "repo1", "machines", "machine1"]
|
run_machine_deploy, "test_op", ["clans", "repo1", "machines", "machine1"]
|
||||||
)
|
)
|
||||||
|
|
||||||
top_level = configured_log_manager.filter([])
|
top_level = configured_log_manager.filter([])
|
||||||
@@ -258,7 +258,7 @@ class TestFilterFunction:
|
|||||||
for repo in repos:
|
for repo in repos:
|
||||||
for machine in machines:
|
for machine in machines:
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine,
|
run_machine_deploy,
|
||||||
f"deploy_{machine}",
|
f"deploy_{machine}",
|
||||||
["clans", repo, "machines", machine],
|
["clans", repo, "machines", machine],
|
||||||
)
|
)
|
||||||
@@ -281,7 +281,7 @@ class TestFilterFunction:
|
|||||||
"""Test filtering with specific date."""
|
"""Test filtering with specific date."""
|
||||||
# Create log file
|
# Create log file
|
||||||
log_file = configured_log_manager.create_log_file(
|
log_file = configured_log_manager.create_log_file(
|
||||||
deploy_machine, "test_op", ["clans", "repo1", "machines", "machine1"]
|
run_machine_deploy, "test_op", ["clans", "repo1", "machines", "machine1"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Filter with the specific date
|
# Filter with the specific date
|
||||||
@@ -308,14 +308,16 @@ class TestGetLogFile:
|
|||||||
"""Test getting log file by operation key."""
|
"""Test getting log file by operation key."""
|
||||||
# Create log file
|
# Create log file
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine, "deploy_wintux", ["clans", "repo1", "machines", "wintux"]
|
run_machine_deploy,
|
||||||
|
"deploy_wintux",
|
||||||
|
["clans", "repo1", "machines", "wintux"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find it by op_key
|
# Find it by op_key
|
||||||
found_log_file = configured_log_manager.get_log_file("deploy_wintux")
|
found_log_file = configured_log_manager.get_log_file("deploy_wintux")
|
||||||
assert found_log_file is not None
|
assert found_log_file is not None
|
||||||
assert found_log_file.op_key == "deploy_wintux"
|
assert found_log_file.op_key == "deploy_wintux"
|
||||||
assert found_log_file.func_name == "deploy_machine"
|
assert found_log_file.func_name == "run_machine_deploy"
|
||||||
|
|
||||||
def test_get_log_file_with_selector(
|
def test_get_log_file_with_selector(
|
||||||
self, configured_log_manager: LogManager
|
self, configured_log_manager: LogManager
|
||||||
@@ -323,10 +325,14 @@ class TestGetLogFile:
|
|||||||
"""Test getting log file with specific selector like example_usage.py."""
|
"""Test getting log file with specific selector like example_usage.py."""
|
||||||
# Create log files in different locations
|
# Create log files in different locations
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine, "deploy_wintux", ["clans", "repo1", "machines", "wintux"]
|
run_machine_deploy,
|
||||||
|
"deploy_wintux",
|
||||||
|
["clans", "repo1", "machines", "wintux"],
|
||||||
)
|
)
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine, "deploy_wintux", ["clans", "repo2", "machines", "wintux"]
|
run_machine_deploy,
|
||||||
|
"deploy_wintux",
|
||||||
|
["clans", "repo2", "machines", "wintux"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find specific one using selector
|
# Find specific one using selector
|
||||||
@@ -341,7 +347,7 @@ class TestGetLogFile:
|
|||||||
"""Test getting log file with specific date."""
|
"""Test getting log file with specific date."""
|
||||||
# Create log file
|
# Create log file
|
||||||
log_file = configured_log_manager.create_log_file(
|
log_file = configured_log_manager.create_log_file(
|
||||||
deploy_machine, "deploy_demo", ["clans", "repo1", "machines", "demo"]
|
run_machine_deploy, "deploy_demo", ["clans", "repo1", "machines", "demo"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find it by op_key and date
|
# Find it by op_key and date
|
||||||
@@ -378,10 +384,10 @@ class TestListLogDays:
|
|||||||
"""Test listing log days when logs exist."""
|
"""Test listing log days when logs exist."""
|
||||||
# Create log files
|
# Create log files
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine, "op1", ["clans", "repo1", "machines", "machine1"]
|
run_machine_deploy, "op1", ["clans", "repo1", "machines", "machine1"]
|
||||||
)
|
)
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine, "op2", ["clans", "repo2", "machines", "machine2"]
|
run_machine_deploy, "op2", ["clans", "repo2", "machines", "machine2"]
|
||||||
)
|
)
|
||||||
|
|
||||||
days = configured_log_manager.list_log_days()
|
days = configured_log_manager.list_log_days()
|
||||||
@@ -406,7 +412,7 @@ class TestApiCompatibility:
|
|||||||
for repo in repos:
|
for repo in repos:
|
||||||
for machine in machines:
|
for machine in machines:
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine,
|
run_machine_deploy,
|
||||||
f"deploy_{machine}",
|
f"deploy_{machine}",
|
||||||
["clans", repo, "machines", machine],
|
["clans", repo, "machines", machine],
|
||||||
)
|
)
|
||||||
@@ -741,19 +747,19 @@ class TestLogFileSorting:
|
|||||||
# This simulates the realistic scenario where the same operation runs on different machines
|
# This simulates the realistic scenario where the same operation runs on different machines
|
||||||
|
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine,
|
run_machine_deploy,
|
||||||
"deploy_operation",
|
"deploy_operation",
|
||||||
["clans", "repo1", "machines", "machine1"],
|
["clans", "repo1", "machines", "machine1"],
|
||||||
)
|
)
|
||||||
|
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine,
|
run_machine_deploy,
|
||||||
"deploy_operation",
|
"deploy_operation",
|
||||||
["clans", "repo1", "machines", "machine2"],
|
["clans", "repo1", "machines", "machine2"],
|
||||||
)
|
)
|
||||||
|
|
||||||
configured_log_manager.create_log_file(
|
configured_log_manager.create_log_file(
|
||||||
deploy_machine,
|
run_machine_deploy,
|
||||||
"deploy_operation",
|
"deploy_operation",
|
||||||
["clans", "repo2", "machines", "machine1"],
|
["clans", "repo2", "machines", "machine1"],
|
||||||
)
|
)
|
||||||
@@ -819,7 +825,7 @@ class TestURLEncoding:
|
|||||||
|
|
||||||
# Create log file with special characters
|
# Create log file with special characters
|
||||||
log_file = configured_log_manager.create_log_file(
|
log_file = configured_log_manager.create_log_file(
|
||||||
deploy_machine,
|
run_machine_deploy,
|
||||||
"deploy_special",
|
"deploy_special",
|
||||||
["clans", special_repo, "machines", special_machine],
|
["clans", special_repo, "machines", special_machine],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class InstallOptions:
|
|||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def install_machine(opts: InstallOptions, target_host: Remote) -> None:
|
def run_machine_install(opts: InstallOptions, target_host: Remote) -> None:
|
||||||
machine = opts.machine
|
machine = opts.machine
|
||||||
|
|
||||||
machine.debug(f"installing {machine.name}")
|
machine.debug(f"installing {machine.name}")
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ def upload_sources(machine: Machine, ssh: Remote) -> str:
|
|||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def deploy_machine(
|
def run_machine_deploy(
|
||||||
machine: Machine, target_host: Remote, build_host: Remote | None
|
machine: Machine, target_host: Remote, build_host: Remote | None
|
||||||
) -> None:
|
) -> None:
|
||||||
with ExitStack() as stack:
|
with ExitStack() as stack:
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ from dataclasses import dataclass, field
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shlex import quote
|
from shlex import quote
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from typing import Literal
|
|
||||||
|
|
||||||
from clan_lib.api import API
|
from clan_lib.api import API
|
||||||
from clan_lib.cmd import ClanCmdError, ClanCmdTimeoutError, CmdOut, RunOpts, run
|
from clan_lib.cmd import ClanCmdError, ClanCmdTimeoutError, CmdOut, RunOpts, run
|
||||||
@@ -74,9 +73,9 @@ class Remote:
|
|||||||
private_key=private_key if private_key is not None else self.private_key,
|
private_key=private_key if private_key is not None else self.private_key,
|
||||||
password=password if password is not None else self.password,
|
password=password if password is not None else self.password,
|
||||||
forward_agent=self.forward_agent,
|
forward_agent=self.forward_agent,
|
||||||
host_key_check=host_key_check
|
host_key_check=(
|
||||||
if host_key_check is not None
|
host_key_check if host_key_check is not None else self.host_key_check
|
||||||
else self.host_key_check,
|
),
|
||||||
verbose_ssh=self.verbose_ssh,
|
verbose_ssh=self.verbose_ssh,
|
||||||
ssh_options=self.ssh_options,
|
ssh_options=self.ssh_options,
|
||||||
tor_socks=tor_socks if tor_socks is not None else self.tor_socks,
|
tor_socks=tor_socks if tor_socks is not None else self.tor_socks,
|
||||||
@@ -425,8 +424,8 @@ class Remote:
|
|||||||
|
|
||||||
self.check_sshpass_errorcode(res)
|
self.check_sshpass_errorcode(res)
|
||||||
|
|
||||||
def is_ssh_reachable(self) -> bool:
|
def check_machine_ssh_reachable(self) -> bool:
|
||||||
return is_ssh_reachable(self)
|
return check_machine_ssh_reachable(self).ok
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -435,10 +434,16 @@ class ConnectionOptions:
|
|||||||
retries: int = 10
|
retries: int = 10
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CheckResult:
|
||||||
|
ok: bool
|
||||||
|
reason: str | None = None
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def can_ssh_login(
|
def check_machine_ssh_login(
|
||||||
remote: Remote, opts: ConnectionOptions | None = None
|
remote: Remote, opts: ConnectionOptions | None = None
|
||||||
) -> Literal["Online", "Offline"]:
|
) -> CheckResult:
|
||||||
if opts is None:
|
if opts is None:
|
||||||
opts = ConnectionOptions()
|
opts = ConnectionOptions()
|
||||||
|
|
||||||
@@ -449,7 +454,7 @@ def can_ssh_login(
|
|||||||
["true"],
|
["true"],
|
||||||
RunOpts(timeout=opts.timeout, needs_user_terminal=True),
|
RunOpts(timeout=opts.timeout, needs_user_terminal=True),
|
||||||
)
|
)
|
||||||
return "Online"
|
return CheckResult(True)
|
||||||
except ClanCmdTimeoutError:
|
except ClanCmdTimeoutError:
|
||||||
pass
|
pass
|
||||||
except ClanCmdError as e:
|
except ClanCmdError as e:
|
||||||
@@ -458,11 +463,13 @@ def can_ssh_login(
|
|||||||
else:
|
else:
|
||||||
time.sleep(opts.timeout)
|
time.sleep(opts.timeout)
|
||||||
|
|
||||||
return "Offline"
|
return CheckResult(False, f"failed after {opts.retries} attempts")
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def is_ssh_reachable(remote: Remote, opts: ConnectionOptions | None = None) -> bool:
|
def check_machine_ssh_reachable(
|
||||||
|
remote: Remote, opts: ConnectionOptions | None = None
|
||||||
|
) -> CheckResult:
|
||||||
if opts is None:
|
if opts is None:
|
||||||
opts = ConnectionOptions()
|
opts = ConnectionOptions()
|
||||||
|
|
||||||
@@ -472,10 +479,10 @@ def is_ssh_reachable(remote: Remote, opts: ConnectionOptions | None = None) -> b
|
|||||||
sock.settimeout(opts.timeout)
|
sock.settimeout(opts.timeout)
|
||||||
try:
|
try:
|
||||||
sock.connect((remote.address, remote.port or 22))
|
sock.connect((remote.address, remote.port or 22))
|
||||||
return True
|
return CheckResult(True)
|
||||||
except (TimeoutError, OSError):
|
except (TimeoutError, OSError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
time.sleep(opts.timeout)
|
time.sleep(opts.timeout)
|
||||||
|
|
||||||
return False
|
return CheckResult(False, f"failed after {opts.retries} attempts")
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ from clan_lib.nix_models.clan import (
|
|||||||
)
|
)
|
||||||
from clan_lib.nix_models.clan import InventoryMachineDeploy as MachineDeploy
|
from clan_lib.nix_models.clan import InventoryMachineDeploy as MachineDeploy
|
||||||
from clan_lib.persist.util import set_value_by_path
|
from clan_lib.persist.util import set_value_by_path
|
||||||
from clan_lib.ssh.remote import Remote, can_ssh_login
|
from clan_lib.ssh.remote import Remote, check_machine_ssh_login
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -190,8 +190,9 @@ def test_clan_create_api(
|
|||||||
target_host = machine.target_host().override(
|
target_host = machine.target_host().override(
|
||||||
private_key=private_key, host_key_check="none"
|
private_key=private_key, host_key_check="none"
|
||||||
)
|
)
|
||||||
result = can_ssh_login(target_host)
|
assert check_machine_ssh_login(target_host).ok, (
|
||||||
assert result == "Online", f"Machine {machine.name} is not online"
|
f"Machine {machine.name} is not online"
|
||||||
|
)
|
||||||
|
|
||||||
ssh_keys = [
|
ssh_keys = [
|
||||||
SSHKeyPair(
|
SSHKeyPair(
|
||||||
|
|||||||
Reference in New Issue
Block a user