feature!: introduce --flake option for setting the flake to use
Having this option available allows for using a flake which isn't in (or
upwards of) the directory the command gets executed in and allows for
using remote flakes.
Also archive the flake to use first and then operate on the archive.
This allows for easily getting the deployment_configuration.json from
the archive and also ensures that once the archiving suceeds, there
shouldn't be issues with the flakes source anymore.
Since now the deployment_configuration.json will always be taken from
the root of the flakes archive and therefore from the root of the flakes
repo, this is a breaking change, since previously it was taken from the
current working directory.
The idea of archiving the flake first and operating on the archive comes
from bij:
221052d846
Moreover introduce helper functions for facilitating recursive options
(i.e. options one can set on root and sub-commands).
This commit is contained in:
parent
d164f502a1
commit
a7772aa154
|
@ -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`. \
|
infra-rebuild accepts optional configuration in a `deployment_configuration.json` present in the flake repos root. \
|
||||||
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.
|
||||||
|
|
|
@ -3,54 +3,108 @@ 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):
|
||||||
|
"""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)
|
@click.group(context_settings={"help_option_names": ["-h", "--help"]}, invoke_without_command=True)
|
||||||
@click.version_option(prog_name="infra-rebuild")
|
@click.version_option(prog_name="infra-rebuild")
|
||||||
def infra_rebuild():
|
@click.pass_context
|
||||||
pass
|
@click.option("--flake", help=flake_option_help_text)
|
||||||
|
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(hosts):
|
def build(ctx, flake, hosts):
|
||||||
operations.run("build", hosts)
|
update_recursive_option(ctx, "flake", flake)
|
||||||
|
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(hosts):
|
def build_vm(ctx, flake, hosts):
|
||||||
operations.run("build-vm", hosts)
|
update_recursive_option(ctx, "flake", flake)
|
||||||
|
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(hosts):
|
def build_vm_with_bootloader(ctx, flake, hosts):
|
||||||
operations.run("build-vm-with-bootloader", hosts)
|
update_recursive_option(ctx, "flake", flake)
|
||||||
|
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(hosts, reboot):
|
def switch(ctx, flake, reboot, hosts):
|
||||||
operations.run("switch", hosts, reboot)
|
update_recursive_option(ctx, "flake", flake)
|
||||||
|
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(hosts, reboot):
|
def boot(ctx, flake, reboot, hosts):
|
||||||
operations.run("boot", hosts, reboot)
|
update_recursive_option(ctx, "flake", flake)
|
||||||
|
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(hosts, reboot):
|
def test(ctx, flake, reboot, hosts):
|
||||||
operations.run("test", hosts, reboot)
|
update_recursive_option(ctx, "flake", flake)
|
||||||
|
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(hosts):
|
def reboot(ctx, flake, hosts):
|
||||||
operations.run("boot", hosts, True)
|
update_recursive_option(ctx, "flake", flake)
|
||||||
|
operations.run("boot", hosts, ctx.obj["flake"], True)
|
||||||
|
|
|
@ -6,7 +6,27 @@ import sys
|
||||||
from infra_rebuild import msg
|
from infra_rebuild import msg
|
||||||
|
|
||||||
|
|
||||||
def run(operation, hosts, reboot=False): # noqa: FBT002 - having reboot as a Boolean positional argument is fine I think # fmt: skip
|
def run(operation, hosts, flake_uri, 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
|
||||||
|
@ -22,23 +42,23 @@ def run(operation, hosts, reboot=False): # noqa: FBT002 - having reboot as a Bo
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if act_remotely:
|
if act_remotely:
|
||||||
remote(operation, host, reboot)
|
remote(operation, host, flake_archive, reboot)
|
||||||
else:
|
else:
|
||||||
local(operation, host)
|
local(operation, host, flake_archive)
|
||||||
|
|
||||||
|
|
||||||
def local(operation, host):
|
def local(operation, host, flake_archive):
|
||||||
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".#{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"{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?
|
||||||
check=False
|
check=False
|
||||||
) # fmt: skip
|
) # fmt: skip
|
||||||
|
|
||||||
|
|
||||||
def remote(operation, host, reboot=False): # noqa: FBT002 - having reboot as a Boolean positional argument is fine I think # 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
|
||||||
config = None
|
config = None
|
||||||
try:
|
try:
|
||||||
with open("deployment_configuration.json") as config_file:
|
with open(f"{flake_archive['path']}/deployment_configuration.json") as config_file:
|
||||||
try:
|
try:
|
||||||
config = json.load(config_file)
|
config = json.load(config_file)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
|
@ -62,7 +82,7 @@ def remote(operation, host, reboot=False): # noqa: FBT002 - having reboot as a
|
||||||
pass
|
pass
|
||||||
if not target_hostname:
|
if not target_hostname:
|
||||||
nix_config_fqdn = subprocess.run(
|
nix_config_fqdn = subprocess.run(
|
||||||
["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?
|
["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?
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
check=False,
|
check=False,
|
||||||
|
@ -119,7 +139,7 @@ def remote(operation, host, reboot=False): # noqa: FBT002 - having reboot as a
|
||||||
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".#{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"{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
|
||||||
env=nixos_rebuild_env,
|
env=nixos_rebuild_env,
|
||||||
check=False,
|
check=False,
|
||||||
) # fmt: skip
|
) # fmt: skip
|
||||||
|
|
Loading…
Reference in a new issue