Compare commits

..

No commits in common. "744bbdb245a8f4da1ca09455d07ffd92f08beb2f" and "d164f502a19a0a143e680033477ad8c6d65799eb" have entirely different histories.

3 changed files with 27 additions and 111 deletions

View file

@ -20,7 +20,7 @@ However to override aspects of the target host for specific or all hosts, infra-
## Configuration ## Configuration
infra-rebuild accepts optional configuration in a `deployment_configuration.json` present in the flake repos root. \ infra-rebuild accepts optional configuration in a `deployment_configuration.json`. \
The following keys are available to be set for configuring various aspects of deployment for specific or all hosts: The following keys are available to be set for configuring various aspects of deployment for specific or all hosts:
- `default.targetPort`: A default port to use for connecting to all host. - `default.targetPort`: A default port to use for connecting to all host.

View file

@ -3,118 +3,54 @@ import click
from infra_rebuild import operations from infra_rebuild import operations
reboot_option_help_text = "Reboot the target hosts after running the operation." reboot_option_help_text = "Reboot the target hosts after running the operation."
flake_option_help_text = "URI of the flake to use. Defaults to the current direcory."
def initialize_recursive_option(ctx, option_name, default_value): @click.group(context_settings={"help_option_names": ["-h", "--help"]}, invoke_without_command=True)
"""Helper function for initializing a recursive option.
Arguments:
`ctx` -- The click context passed to the calling command.
`option_name` -- The name of the option in question.
`default_value` -- A default value for the option.
Simply sets the value of the recursive option (`ctx.obj[option_name]`) to `default_value`.
"""
ctx.obj[option_name] = default_value
def update_recursive_option(ctx, option_name, given_value):
"""Helper function for updating a recursive option.
Arguments:
ctx -- The click context passed to the calling command.
option_name -- The name of the option in question.
given_value -- The value the calling command received for the option.
It overrides the current value for the recursive option (`ctx.obj[option_name]`) with `given_value`, if `given_value` isn't `None`.
This ensure precedence of the innermost command (since the innermost command would call this function last).
""" # noqa: E501
if given_value is not None:
ctx.obj[option_name] = given_value
@click.group(
context_settings={"help_option_names": ["-h", "--help"]},
invoke_without_command=True,
help="""Runs COMMAND for each given host.
The commands mirror the operations of nixos-rebuild (with the exception of reboot, which is simply a convenience alias for "boot --reboot").
For commands acting remotely (boot, reboot, switch, test) infra-rebuild tries to be useful without any configuration by taking the FQDN from the hosts NixOS configuration as the target host.
However infra-rebuild also accepts configuration to override various aspects of the target host for specific or all hosts.
See the documentation on configuration for more information.""", # noqa: E501
)
@click.version_option(prog_name="infra-rebuild") @click.version_option(prog_name="infra-rebuild")
@click.pass_context def infra_rebuild():
@click.option("--flake", help=flake_option_help_text) pass
def infra_rebuild(ctx, flake):
ctx.ensure_object(dict)
initialize_recursive_option(ctx, "flake", ".")
update_recursive_option(ctx, "flake", flake)
@infra_rebuild.command() @infra_rebuild.command()
@click.pass_context
@click.option("--flake", help=flake_option_help_text)
@click.argument("hosts", nargs=-1) @click.argument("hosts", nargs=-1)
def build(ctx, flake, hosts): def build(hosts):
update_recursive_option(ctx, "flake", flake) operations.run("build", hosts)
operations.run("build", hosts, ctx.obj["flake"])
@infra_rebuild.command() @infra_rebuild.command()
@click.pass_context
@click.option("--flake", help=flake_option_help_text)
@click.argument("hosts", nargs=-1) @click.argument("hosts", nargs=-1)
def build_vm(ctx, flake, hosts): def build_vm(hosts):
update_recursive_option(ctx, "flake", flake) operations.run("build-vm", hosts)
operations.run("build-vm", hosts, ctx.obj["flake"])
@infra_rebuild.command() @infra_rebuild.command()
@click.pass_context
@click.option("--flake", help=flake_option_help_text)
@click.argument("hosts", nargs=-1) @click.argument("hosts", nargs=-1)
def build_vm_with_bootloader(ctx, flake, hosts): def build_vm_with_bootloader(hosts):
update_recursive_option(ctx, "flake", flake) operations.run("build-vm-with-bootloader", hosts)
operations.run("build-vm-with-bootloader", hosts, ctx.obj["flake"])
@infra_rebuild.command() @infra_rebuild.command()
@click.pass_context
@click.option("--flake", help=flake_option_help_text)
@click.option("--reboot", is_flag=True, help=reboot_option_help_text) @click.option("--reboot", is_flag=True, help=reboot_option_help_text)
@click.argument("hosts", nargs=-1) @click.argument("hosts", nargs=-1)
def switch(ctx, flake, reboot, hosts): def switch(hosts, reboot):
update_recursive_option(ctx, "flake", flake) operations.run("switch", hosts, reboot)
operations.run("switch", hosts, ctx.obj["flake"], reboot)
@infra_rebuild.command() @infra_rebuild.command()
@click.pass_context
@click.option("--flake", help=flake_option_help_text)
@click.option("--reboot", is_flag=True, help=reboot_option_help_text) @click.option("--reboot", is_flag=True, help=reboot_option_help_text)
@click.argument("hosts", nargs=-1) @click.argument("hosts", nargs=-1)
def boot(ctx, flake, reboot, hosts): def boot(hosts, reboot):
update_recursive_option(ctx, "flake", flake) operations.run("boot", hosts, reboot)
operations.run("boot", hosts, ctx.obj["flake"], reboot)
@infra_rebuild.command() @infra_rebuild.command()
@click.pass_context
@click.option("--flake", help=flake_option_help_text)
@click.option("--reboot", is_flag=True, help=reboot_option_help_text) @click.option("--reboot", is_flag=True, help=reboot_option_help_text)
@click.argument("hosts", nargs=-1) @click.argument("hosts", nargs=-1)
def test(ctx, flake, reboot, hosts): def test(hosts, reboot):
update_recursive_option(ctx, "flake", flake) operations.run("test", hosts, reboot)
operations.run("test", hosts, ctx.obj["flake"], reboot)
@infra_rebuild.command(short_help=" ", help="This operation is a convenience alias for boot --reboot.") @infra_rebuild.command(short_help=" ", help="This operation is a convenience alias for boot --reboot.")
@click.pass_context
@click.option("--flake", help=flake_option_help_text)
@click.argument("hosts", nargs=-1) @click.argument("hosts", nargs=-1)
def reboot(ctx, flake, hosts): def reboot(hosts):
update_recursive_option(ctx, "flake", flake) operations.run("boot", hosts, True)
operations.run("boot", hosts, ctx.obj["flake"], True)

View file

@ -6,27 +6,7 @@ import sys
from infra_rebuild import msg from infra_rebuild import msg
def run(operation, hosts, flake_uri, reboot=False): # noqa: FBT002 - having reboot as a Boolean positional argument is fine I think # fmt: skip def run(operation, hosts, reboot=False): # noqa: FBT002 - having reboot as a Boolean positional argument is fine I think # fmt: skip
try:
flake_archive_json = subprocess.run(
["nix", "flake", "archive", "--json", f"{flake_uri}"], # noqa: S607, S603 - can't know what the users system looks like, maybe do some kind of validation for the flake_uri in the future?
capture_output=True,
text=True,
check=True
) # fmt: skip
try:
flake_archive = json.loads(flake_archive_json.stdout)
except json.JSONDecodeError:
msg.error("Internal Error: Couldn't parse the output of \"nix flake archive\".")
sys.exit(1)
if not flake_archive["path"]:
msg.error("Internal Error: The \"nix flake archive\" output doesn't include a path.")
sys.exit(1)
except subprocess.CalledProcessError:
msg.error("Error: Couldn't archive the provided flake.")
sys.exit(1)
match operation: match operation:
case "build" | "build-vm" | "build-vm-with-bootloader": case "build" | "build-vm" | "build-vm-with-bootloader":
act_remotely = False act_remotely = False
@ -42,23 +22,23 @@ def run(operation, hosts, flake_uri, reboot=False): # noqa: FBT002 - having reb
continue continue
if act_remotely: if act_remotely:
remote(operation, host, flake_archive, reboot) remote(operation, host, reboot)
else: else:
local(operation, host, flake_archive) local(operation, host)
def local(operation, host, flake_archive): def local(operation, host):
msg.info(f"Running nixos-rebuild {operation} for {host}...") msg.info(f"Running nixos-rebuild {operation} for {host}...")
subprocess.run( subprocess.run(
["nixos-rebuild", operation, "--flake", f"{flake_archive['path']}#{host}"], # noqa: S607, S603 - can't know what the users system looks like, maybe do some kind of validation for the given host in the future? ["nixos-rebuild", operation, "--flake", f".#{host}"], # noqa: S607, S603 - can't know what the users system looks like, maybe do some kind of validation for the given host in the future?
check=False check=False
) # fmt: skip ) # fmt: skip
def remote(operation, host, flake_archive, reboot=False): # noqa: FBT002 - having reboot as a Boolean positional argument is fine I think # fmt: skip def remote(operation, host, reboot=False): # noqa: FBT002 - having reboot as a Boolean positional argument is fine I think # fmt: skip
config = None config = None
try: try:
with open(f"{flake_archive['path']}/deployment_configuration.json") as config_file: with open("deployment_configuration.json") as config_file:
try: try:
config = json.load(config_file) config = json.load(config_file)
except json.JSONDecodeError: except json.JSONDecodeError:
@ -82,7 +62,7 @@ def remote(operation, host, flake_archive, reboot=False): # noqa: FBT002 - havi
pass pass
if not target_hostname: if not target_hostname:
nix_config_fqdn = subprocess.run( nix_config_fqdn = subprocess.run(
["nix", "eval", "--raw", f"{flake_archive['path']}#nixosConfigurations.{host}.config.networking.fqdn"], # noqa: S607, S603 - can't know what the users system looks like, maybe do some kind of validation for host in the future? ["nix", "eval", "--raw", f".#nixosConfigurations.{host}.config.networking.fqdn"], # noqa: S607, S603 - can't know what the users system looks like, maybe do some kind of validation for host in the future?
capture_output=True, capture_output=True,
text=True, text=True,
check=False, check=False,
@ -139,7 +119,7 @@ def remote(operation, host, flake_archive, reboot=False): # noqa: FBT002 - havi
msg.info(f"Running nixos-rebuild {operation} for {host} on {target_host}...") msg.info(f"Running nixos-rebuild {operation} for {host} on {target_host}...")
nixos_rebuild = subprocess.run( nixos_rebuild = subprocess.run(
["nixos-rebuild", operation, "--flake", f"{flake_archive['path']}#{host}", "--target-host", target_host, "--use-substitutes", "--use-remote-sudo"], # noqa: S607, S603, E501 - can't know what the users system looks like, maybe do some kind of validation for the given host in the future?, not breaking up the command makes it more readable ["nixos-rebuild", operation, "--flake", f".#{host}", "--target-host", target_host, "--use-substitutes", "--use-remote-sudo"], # noqa: S607, S603, E501 - can't know what the users system looks like, maybe do some kind of validation for the given host in the future?, not breaking up the command makes it more readable
env=nixos_rebuild_env, env=nixos_rebuild_env,
check=False, check=False,
) # fmt: skip ) # fmt: skip