infra/tasks.py

237 lines
6.1 KiB
Python
Raw Normal View History

2021-10-21 11:09:52 +02:00
#!/usr/bin/env python3
2021-10-24 01:04:22 +02:00
import json
2023-01-16 09:37:38 +10:00
import os
2022-12-31 07:24:17 +01:00
import subprocess
import sys
import tempfile
2023-03-12 15:02:16 +10:00
from pathlib import Path
from typing import List
2022-12-31 07:24:17 +01:00
from deploykit import DeployGroup, DeployHost
from invoke import task
2021-10-21 11:09:52 +02:00
2023-03-12 15:02:16 +10:00
ROOT = Path(__file__).parent.resolve()
os.chdir(ROOT)
2021-10-21 11:09:52 +02:00
2023-03-17 13:36:07 +10:00
2023-03-12 15:02:16 +10:00
# Deploy to all hosts in parallel
2021-10-21 11:09:52 +02:00
def deploy_nixos(hosts: List[DeployHost]) -> None:
g = DeployGroup(hosts)
2022-01-15 13:38:30 +01:00
2023-03-06 16:42:08 +01:00
res = subprocess.run(
["nix", "flake", "metadata", "--json"],
check=True,
text=True,
stdout=subprocess.PIPE,
)
data = json.loads(res.stdout)
path = data["path"]
2021-10-21 11:09:52 +02:00
def deploy(h: DeployHost) -> None:
h.run_local(
f"rsync --rsync-path='sudo rsync' --checksum -vaF --delete -e ssh {path}/ {h.host}:/etc/nixos"
)
2021-10-21 11:09:52 +02:00
hostname = h.host.replace(".nix-community.org", "")
h.run(
[
"sudo",
"nixos-rebuild",
"switch",
"--option",
"accept-flake-config",
"true",
"--flake",
2023-03-25 10:44:53 +00:00
f"/etc/nixos#{hostname}",
]
)
2022-01-15 13:38:30 +01:00
2021-10-21 11:09:52 +02:00
g.run_function(deploy)
2022-10-25 09:55:14 +02:00
@task
def update_sops_files(c):
"""
Update all sops yaml and json files according to .sops.yaml rules
"""
c.run(
"""
find . \
-type f \
2022-11-17 08:57:22 +10:00
\( -iname '*.enc.json' -o -iname 'secrets.yaml' \) \
-exec sops updatekeys --yes {} \;
2022-10-25 09:55:14 +02:00
"""
)
2022-12-31 07:24:17 +01:00
2022-12-30 20:51:58 +01:00
@task
def print_keys(c, hosts=""):
2022-12-30 20:51:58 +01:00
"""
Decrypt host private key, print ssh and age public keys. Use inv print-keys --hosts build01
2022-12-30 20:51:58 +01:00
"""
g = DeployGroup(get_hosts(hosts))
def key(h: DeployHost) -> None:
hostname = h.host.replace(".nix-community.org", "")
with tempfile.TemporaryDirectory() as tmpdir:
decrypt_host_key(c, hostname, tmpdir)
pubkey = subprocess.run(
["ssh-keygen", "-y", "-f", f"{tmpdir}/etc/ssh/ssh_host_ed25519_key"],
stdout=subprocess.PIPE,
text=True,
check=True,
)
print("###### Public keys ######")
print(pubkey.stdout)
print("###### Age keys ######")
subprocess.run(
["ssh-to-age"],
input=pubkey.stdout,
check=True,
text=True,
)
g.run_function(key)
2022-12-30 20:51:58 +01:00
2022-10-25 09:55:14 +02:00
2023-03-15 10:50:57 +10:00
@task
def update_terraform(c):
"""
Update terraform devshell flake
"""
with c.cd("terraform"):
c.run(
"""
system="$(nix eval --impure --raw --expr 'builtins.currentSystem')"
old="$(nix build --no-link --print-out-paths ".#devShells.${system}.default")"
nix flake update --commit-lock-file
new="$(nix build --no-link --print-out-paths ".#devShells.${system}.default")"
commit="$(git log --pretty=format:%B -1)"
diff="$(nix store diff-closures "${old}" "${new}" | awk -F ',' '/terraform/ && /→/ {print $1}')"
git commit --amend -m "${commit}" -m "Terraform updates:" -m "${diff}"
"""
)
@task
def mkdocs(c):
"""
Serve docs (mkdoc serve)
"""
c.run("nix develop .#pages -c mkdocs serve")
2021-10-24 01:04:22 +02:00
def get_hosts(hosts: str) -> List[DeployHost]:
2021-10-21 11:09:52 +02:00
if hosts == "":
return [DeployHost(f"build{n + 1:02d}.nix-community.org") for n in range(4)]
2021-10-21 11:09:52 +02:00
return [DeployHost(f"{h}.nix-community.org") for h in hosts.split(",")]
2021-10-21 11:09:52 +02:00
@task
2022-01-15 13:38:30 +01:00
def deploy(c, hosts=""):
2021-10-21 11:09:52 +02:00
"""
2023-01-07 07:37:07 +10:00
Deploy to all servers. Use inv deploy --hosts build01 to deploy to a single server
2021-10-21 11:09:52 +02:00
"""
deploy_nixos(get_hosts(hosts))
def decrypt_host_key(c, hostname, tmpdir):
os.mkdir(f"{tmpdir}/etc")
os.mkdir(f"{tmpdir}/etc/ssh")
os.umask(0o177)
c.run(
f"sops --extract '[\"ssh_host_ed25519_key\"]' --decrypt {ROOT}/{hostname}/secrets.yaml > {tmpdir}/etc/ssh/ssh_host_ed25519_key"
)
@task
def install(c, hosts=""):
"""
Decrypt host private key, install with nixos-anywhere. Use inv install --hosts build01
"""
g = DeployGroup(get_hosts(hosts))
def anywhere(h: DeployHost) -> None:
hostname = h.host.replace(".nix-community.org", "")
with tempfile.TemporaryDirectory() as tmpdir:
decrypt_host_key(c, hostname, tmpdir)
c.run(
f"nix run github:numtide/nixos-anywhere#nixos-anywhere -- --extra-files {tmpdir} --flake .#{hostname} {h.host}"
)
g.run_function(anywhere)
@task
def build_local(c, hosts=""):
"""
2023-01-07 07:37:07 +10:00
Build all servers. Use inv build-local --hosts build01 to build a single server
"""
g = DeployGroup(get_hosts(hosts))
def build_local(h: DeployHost) -> None:
hostname = h.host.replace(".nix-community.org", "")
h.run_local(
[
"nixos-rebuild",
"build",
"--option",
"accept-flake-config",
"true",
"--flake",
f".#{hostname}",
]
)
g.run_function(build_local)
2021-10-21 11:09:52 +02:00
def wait_for_port(host: str, port: int, shutdown: bool = False) -> None:
2022-12-31 07:24:17 +01:00
import socket
import time
2021-10-21 11:09:52 +02:00
while True:
try:
with socket.create_connection((host, port), timeout=1):
if shutdown:
time.sleep(1)
sys.stdout.write(".")
sys.stdout.flush()
else:
break
2022-12-31 07:24:17 +01:00
except OSError:
2021-10-21 11:09:52 +02:00
if shutdown:
break
else:
time.sleep(0.01)
sys.stdout.write(".")
sys.stdout.flush()
@task
def reboot(c, hosts=""):
"""
Reboot hosts. example usage: inv reboot --hosts build01,build02
"""
2021-10-24 01:31:40 +02:00
for h in get_hosts(hosts):
h.run("sudo reboot &")
2021-10-21 11:09:52 +02:00
print(f"Wait for {h.host} to shutdown", end="")
sys.stdout.flush()
wait_for_port(h.host, h.port, shutdown=True)
print("")
print(f"Wait for {h.host} to start", end="")
sys.stdout.flush()
wait_for_port(h.host, h.port)
print("")
@task
def cleanup_gcroots(c, hosts=""):
g = DeployGroup(get_hosts(hosts))
g.run("sudo find /nix/var/nix/gcroots/auto -type s -delete")
g.run("sudo systemctl restart nix-gc")