machines-config/scripts/fresh_install.py

184 lines
4.3 KiB
Python
Raw Permalink Normal View History

2025-01-06 22:44:27 +01:00
#!/usr/bin/env python3
import argparse
import json
2025-01-09 17:34:18 +01:00
import pathlib
import shutil
2025-01-07 20:05:09 +01:00
import socket
2025-01-07 16:18:04 +01:00
import time
2025-01-06 22:44:27 +01:00
from subprocess import check_output
2025-01-09 17:34:18 +01:00
ROOT_DIR = pathlib.Path(__file__).parent.parent
KEYS_FILE = ROOT_DIR / "secrets" / "keys.json"
2025-01-07 16:18:04 +01:00
2025-01-06 22:44:27 +01:00
def _get_available_machines() -> list:
2025-01-07 16:18:04 +01:00
output = check_output(["nix", "flake", "show", "--json"])
2025-01-06 22:44:27 +01:00
parsed_output = json.loads(output)
2025-01-07 16:18:04 +01:00
machines = parsed_output.get("nixosConfigurations", dict()).keys()
2025-01-06 22:44:27 +01:00
return list(machines)
2025-01-07 16:18:04 +01:00
2025-01-09 17:34:18 +01:00
def _is_valid_ip(ip: str) -> bool:
2025-01-06 22:44:27 +01:00
try:
2025-01-07 20:05:09 +01:00
socket.inet_aton(ip)
return True
2025-01-09 17:34:18 +01:00
except socket.error:
return False
2025-01-06 22:44:27 +01:00
2025-01-07 16:18:04 +01:00
2025-01-07 16:16:58 +01:00
def _check_ssh_connection(ip: str) -> bool:
try:
2025-01-09 17:34:18 +01:00
check_output(["ssh", f"krop@{ip}", "echo", "Connected"])
2025-01-07 16:16:58 +01:00
return True
except Exception:
return False
2025-01-09 17:34:18 +01:00
def add_key_to_secrets(machine_name: str, key: str):
keys = json.loads(KEYS_FILE.read_text())
if keys.get("servers").get(machine_name):
raise ValueError(f"Key for {machine_name} already exists, remove it first")
keys["servers"][machine_name] = key
for secret in keys.get("secrets"):
keys["secrets"][secret].append(f"servers:{machine_name}")
KEYS_FILE.write_text(json.dumps(keys, indent=2))
def rekey_secrets():
agenix_bin = shutil.which("agenix")
check_output([agenix_bin, "-r"], cwd=ROOT_DIR / "secrets")
2025-01-07 16:18:04 +01:00
2025-01-07 16:16:58 +01:00
def bootstrap_machine(ip: str):
check_output(
[
2025-01-07 16:18:04 +01:00
"nix",
"run",
"github:nix-community/nixos-anywhere",
"--",
"--flake",
2025-01-09 17:34:18 +01:00
".#bootstrap",
2025-01-07 16:18:04 +01:00
"--target-host",
f"root@{ip}",
"--build-on-remote",
2025-01-07 16:16:58 +01:00
]
)
2025-01-09 17:34:18 +01:00
def install_machine(machine_name: str, ip: str):
check_output(
[
"nixos-rebuild",
"boot",
"--flake",
f".#{machine_name}",
"--fast",
"--target-host",
f"krop@{ip}",
"--build-host",
f"krop@{ip}",
"--use-remote-sudo",
]
)
2025-01-07 16:16:58 +01:00
def get_ssh_key(ip: str) -> str:
"""
This function uses machines ssh-keyscan to get the ssh key and then get the ed25519 key
"""
2025-01-09 17:34:18 +01:00
ssh_keys = (
check_output(
[
"ssh-keyscan",
"-q",
"-t",
"ed25519",
ip,
]
)
.decode("utf-8")
.strip()
.splitlines()
)
2025-01-07 20:05:09 +01:00
if len(ssh_keys) != 1:
raise ValueError("Exactly one key should be returned")
key = ssh_keys.pop().lstrip(f"{ip} ").strip()
2025-01-07 16:16:58 +01:00
2025-01-07 20:05:09 +01:00
return key
2025-01-07 16:16:58 +01:00
2025-01-09 17:34:18 +01:00
2025-01-06 22:44:27 +01:00
def get_machine_config(machine_name: str) -> dict:
2025-01-07 16:18:04 +01:00
output = check_output(
[
"nix",
"eval",
"--json",
f".#nixosConfigurations.{machine_name}.config.kropcloud",
]
)
2025-01-06 22:44:27 +01:00
return json.loads(output)
2025-01-09 17:34:18 +01:00
def reboot_machine(ip: str):
check_output(
[
"ssh",
f"krop@{ip}",
"sudo",
"reboot",
]
)
2025-01-06 22:44:27 +01:00
def main() -> int:
2025-01-07 16:18:04 +01:00
parser = argparse.ArgumentParser(description="Install a machine")
parser.add_argument(
"machine_name", type=str, help="The name of the machine to install"
)
parser.add_argument("machine_ip", type=str, help="The ip of the machine to install")
2025-01-06 22:44:27 +01:00
args = parser.parse_args()
machine_name = args.machine_name
2025-01-07 16:18:04 +01:00
if machine_name not in _get_available_machines():
raise ValueError(
f"Machine {machine_name} not found, available machines are: {_get_available_machines()}"
)
2025-01-06 22:44:27 +01:00
2025-01-09 17:34:18 +01:00
machine_ip = args.machine_ip
if not _is_valid_ip(machine_ip):
raise ValueError(f"Invalid IP address {machine_ip}")
2025-01-06 22:44:27 +01:00
2025-01-09 17:34:18 +01:00
print(f"Bootstrapping machine {machine_ip}")
bootstrap_machine(machine_ip)
2025-01-07 20:05:09 +01:00
print("Machine bootstrapped")
2025-01-06 22:44:27 +01:00
2025-01-07 20:05:09 +01:00
print("Waiting for ssh connection")
2025-01-09 17:34:18 +01:00
while not _check_ssh_connection(machine_ip):
2025-01-07 16:16:58 +01:00
time.sleep(5)
2025-01-07 20:05:09 +01:00
print("Machine is up and running")
2025-01-06 22:44:27 +01:00
2025-01-07 20:05:09 +01:00
print("Getting ssh key")
2025-01-09 17:34:18 +01:00
ssh_key = get_ssh_key(machine_ip)
2025-01-06 22:44:27 +01:00
2025-01-09 17:34:18 +01:00
print(f"SSH key: {ssh_key}")
2025-01-07 20:05:09 +01:00
print("Adding ssh key to secrets")
2025-01-09 17:34:18 +01:00
add_key_to_secrets(machine_name, ssh_key)
2025-01-07 20:05:09 +01:00
rekey_secrets()
2025-01-07 16:16:58 +01:00
2025-01-09 17:34:18 +01:00
print("Installing machine")
install_machine(machine_name, machine_ip)
print("Machine installed, rebooting")
reboot_machine(machine_ip)
print("")
2025-01-06 22:44:27 +01:00
return 0
2025-01-07 16:18:04 +01:00
if __name__ == "__main__":
raise SystemExit(main())