summaryrefslogblamecommitdiffstats
path: root/bin/for-servers
blob: 4a975cf5ffebcb344bcac977d1bd3dae9b91a6d8 (plain) (tree)

















































































































                                                                                     
#!/usr/bin/env python3
import asyncio
import os
import sys
from typing import Any, Callable, Coroutine


HOST_COLORS = {
    "tozt": "\x1b[1;33m",
    "mail": "\x1b[32m",
    "partofme": "\x1b[35m",
}


def color_host(host: str):
    return HOST_COLORS[host] + host + "\x1b[m"


async def unlock_rbw():
    proc = await asyncio.create_subprocess_exec("rbw", "unlock")
    await proc.wait()


async def get_password(host: str):
    proc = await asyncio.create_subprocess_exec(
        "rbw",
        "get",
        host,
        os.environ["USER"],
        stdout=asyncio.subprocess.PIPE,
    )

    stdout, _ = await proc.communicate()
    return stdout.rstrip()


async def read_stream(
    stream: asyncio.StreamReader,
    print_cb: Callable[[str], None],
    sudo_cb: Coroutine[Any, Any, None] | None,
):
    buf = b""
    while True:
        read = await stream.read(1024)
        if len(read) == 0:
            if len(buf) > 0:
                print_cb(buf.decode())
            break

        buf += read
        lines = buf.split(b"\n")
        buf = lines.pop()
        for line in lines:
            print_cb(line.decode())

        if sudo_cb and buf == f"[sudo] password for {os.environ['USER']}: ".encode():
            await sudo_cb
            sudo_cb = None
            buf = b""


async def puppet_tozt(host: str):
    password = await get_password(host)

    proc = await asyncio.create_subprocess_exec(
        "ssh",
        host,
        "sudo",
        "--stdin",
        *sys.argv[1:],
        stdin=asyncio.subprocess.PIPE,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
    )
    assert proc.stdout is not None
    assert proc.stderr is not None

    colored_host = color_host(host)

    async def sudo_cb():
        assert proc.stdin is not None
        proc.stdin.write(password + b"\n")
        await proc.stdin.drain()

    await asyncio.gather(
        read_stream(
            proc.stdout,
            lambda line: print(f"[{colored_host}:out] {line}"),
            None,
        ),
        read_stream(
            proc.stderr,
            lambda line: print(f"[{colored_host}:\x1b[31merr\x1b[m] {line}"),
            sudo_cb(),
        ),
    )

    ret = await proc.wait()
    if ret == 0:
        print(f"[{colored_host}] Exited successfully")
    else:
        print(f"[{colored_host}] \x1b[31mExited with code {ret}\x1b[m")


async def main():
    await unlock_rbw()
    await asyncio.gather(
        puppet_tozt("tozt"),
        puppet_tozt("mail"),
        puppet_tozt("partofme"),
    )


asyncio.run(main())