nixos-rebuild-ng: fix non-flake remote build evaluation

Fix: #381457.
This commit is contained in:
Thiago Kenji Okada
2025-02-12 18:26:10 +00:00
parent dcb8946a58
commit 5df5ff8fd2
4 changed files with 211 additions and 13 deletions
@@ -389,8 +389,6 @@ def execute(argv: list[str]) -> None:
dry_run = action == Action.DRY_BUILD
no_link = action in (Action.SWITCH, Action.BOOT)
build_flags |= {"no_out_link": no_link, "dry_run": dry_run}
flake_build_flags |= {"no_link": no_link, "dry_run": dry_run}
rollback = bool(args.rollback)
def validate_image_variant(variants: ImageVariants) -> None:
@@ -444,29 +442,32 @@ def execute(argv: list[str]) -> None:
flake,
build_host,
eval_flags=flake_common_flags,
flake_build_flags=flake_build_flags,
flake_build_flags=flake_build_flags
| {"no_link": no_link, "dry_run": dry_run},
copy_flags=copy_flags,
)
case (_, False, None, Flake(_)):
path_to_config = nix.build_flake(
attr,
flake,
flake_build_flags=flake_build_flags,
flake_build_flags=flake_build_flags
| {"no_link": no_link, "dry_run": dry_run},
)
case (_, False, Remote(_), None):
path_to_config = nix.build_remote(
attr,
build_attr,
build_host,
instantiate_flags=common_flags,
realise_flags=common_flags,
instantiate_flags=build_flags,
copy_flags=copy_flags,
build_flags=build_flags,
)
case (_, False, None, None):
path_to_config = nix.build(
attr,
build_attr,
build_flags=build_flags,
build_flags=build_flags
| {"no_out_link": no_link, "dry_run": dry_run},
)
case never:
# should never happen, but mypy is not smart enough to
@@ -77,7 +77,7 @@ def build_remote(
attr: str,
build_attr: BuildAttr,
build_host: Remote | None,
build_flags: Args | None = None,
realise_flags: Args | None = None,
instantiate_flags: Args | None = None,
copy_flags: Args | None = None,
) -> Path:
@@ -112,7 +112,7 @@ def build_remote(
drv,
"--add-root",
remote_tmpdir / uuid4().hex,
*dict_to_flags(build_flags),
*dict_to_flags(realise_flags),
],
remote=build_host,
stdout=PIPE,
@@ -1,5 +1,6 @@
import logging
import textwrap
import uuid
from pathlib import Path
from subprocess import PIPE, CompletedProcess
from typing import Any
@@ -390,6 +391,202 @@ def test_execute_nix_switch_flake(mock_run: Any, tmp_path: Path) -> None:
)
@patch.dict(nr.process.os.environ, {}, clear=True)
@patch(get_qualified_name(nr.process.subprocess.run), autospec=True)
@patch(get_qualified_name(nr.cleanup_ssh, nr), autospec=True)
@patch(get_qualified_name(nr.nix.uuid4, nr.nix), autospec=True)
def test_execute_nix_switch_build_target_host(
mock_uuid4: Any,
mock_cleanup_ssh: Any,
mock_run: Any,
tmp_path: Path,
) -> None:
config_path = tmp_path / "test"
config_path.touch()
def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]:
if args[0] == "nix":
return CompletedProcess([], 0, str(config_path))
elif args[0] == "nix-instantiate" and "--find-file" in args:
return CompletedProcess([], 1)
elif args[0] == "nix-instantiate":
return CompletedProcess([], 0, str(config_path))
elif args[0] == "ssh" and "nix-store" in args:
return CompletedProcess([], 0, "/tmp/tmpdir/config")
elif args[0] == "ssh" and "mktemp" in args:
return CompletedProcess([], 0, "/tmp/tmpdir")
elif args[0] == "ssh" and "readlink" in args:
return CompletedProcess([], 0, str(config_path))
else:
return CompletedProcess([], 0)
mock_run.side_effect = run_side_effect
mock_uuid4.return_value = uuid.UUID(int=0)
nr.execute(
[
"nixos-rebuild",
"switch",
"--no-flake",
"--sudo",
"--build-host",
"user@build-host",
"--target-host",
"user@target-host",
"--no-reexec",
# https://github.com/NixOS/nixpkgs/issues/381457
"-I",
"nixos-config=./configuration.nix",
"-I",
"nixpkgs=$HOME/.nix-defexpr/channels/pinned_nixpkgs",
]
)
assert mock_run.call_count == 10
mock_run.assert_has_calls(
[
call(
[
"nix-instantiate",
"--find-file",
"nixpkgs",
"--include",
"nixos-config=./configuration.nix",
"--include",
"nixpkgs=$HOME/.nix-defexpr/channels/pinned_nixpkgs",
],
check=False,
stdout=PIPE,
**DEFAULT_RUN_KWARGS,
),
call(
[
"nix-instantiate",
"<nixpkgs/nixos>",
"--attr",
"config.system.build.toplevel",
"--add-root",
nr.tmpdir.TMPDIR_PATH / "00000000000000000000000000000000",
"--include",
"nixos-config=./configuration.nix",
"--include",
"nixpkgs=$HOME/.nix-defexpr/channels/pinned_nixpkgs",
],
check=True,
stdout=PIPE,
**DEFAULT_RUN_KWARGS,
),
call(
["nix-copy-closure", "--to", "user@build-host", config_path],
check=True,
**DEFAULT_RUN_KWARGS,
),
call(
[
"ssh",
*nr.process.SSH_DEFAULT_OPTS,
"user@build-host",
"--",
"mktemp",
"-d",
"-t",
"nixos-rebuild.XXXXX",
],
check=True,
stdout=PIPE,
**DEFAULT_RUN_KWARGS,
),
call(
[
"ssh",
*nr.process.SSH_DEFAULT_OPTS,
"user@build-host",
"--",
"nix-store",
"--realise",
str(config_path),
"--add-root",
"/tmp/tmpdir/00000000000000000000000000000000",
],
check=True,
stdout=PIPE,
**DEFAULT_RUN_KWARGS,
),
call(
[
"ssh",
*nr.process.SSH_DEFAULT_OPTS,
"user@build-host",
"--",
"readlink",
"-f",
"/tmp/tmpdir/config",
],
check=True,
stdout=PIPE,
**DEFAULT_RUN_KWARGS,
),
call(
[
"ssh",
*nr.process.SSH_DEFAULT_OPTS,
"user@build-host",
"--",
"rm",
"-rf",
"/tmp/tmpdir",
],
check=False,
**DEFAULT_RUN_KWARGS,
),
call(
[
"nix",
"copy",
"--from",
"ssh://user@build-host",
"--to",
"ssh://user@target-host",
config_path,
],
check=True,
**DEFAULT_RUN_KWARGS,
),
call(
[
"ssh",
*nr.process.SSH_DEFAULT_OPTS,
"user@target-host",
"--",
"sudo",
"nix-env",
"-p",
"/nix/var/nix/profiles/system",
"--set",
str(config_path),
],
check=True,
**DEFAULT_RUN_KWARGS,
),
call(
[
"ssh",
*nr.process.SSH_DEFAULT_OPTS,
"user@target-host",
"--",
"sudo",
"env",
"NIXOS_INSTALL_BOOTLOADER=0",
str(config_path / "bin/switch-to-configuration"),
"switch",
],
check=True,
**DEFAULT_RUN_KWARGS,
),
]
)
@patch.dict(nr.process.os.environ, {}, clear=True)
@patch(get_qualified_name(nr.process.subprocess.run), autospec=True)
@patch(get_qualified_name(nr.cleanup_ssh, nr), autospec=True)
@@ -469,7 +666,7 @@ def test_execute_nix_switch_flake_target_host(
"sudo",
"env",
"NIXOS_INSTALL_BOOTLOADER=0",
f"{config_path / 'bin/switch-to-configuration'}",
str(config_path / "bin/switch-to-configuration"),
"switch",
],
check=True,
@@ -493,7 +690,7 @@ def test_execute_nix_switch_flake_build_host(
def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]:
if args[0] == "nix" and "eval" in args:
return CompletedProcess([], 0, str(config_path))
if args[0] == "ssh" and "nix" in args:
elif args[0] == "ssh" and "nix" in args:
return CompletedProcess([], 0, str(config_path))
else:
return CompletedProcess([], 0)
@@ -104,7 +104,7 @@ def test_build_remote(mock_uuid4: Any, mock_run: Any, monkeypatch: MonkeyPatch)
"config.system.build.toplevel",
m.BuildAttr("<nixpkgs/nixos>", "preAttr"),
build_host,
build_flags={"build": True},
realise_flags={"realise": True},
instantiate_flags={"inst": True},
copy_flags={"copy": True},
) == Path("/path/to/config")
@@ -147,7 +147,7 @@ def test_build_remote(mock_uuid4: Any, mock_run: Any, monkeypatch: MonkeyPatch)
Path("/path/to/file"),
"--add-root",
Path("/tmp/tmpdir/00000000000000000000000000000002"),
"--build",
"--realise",
],
remote=build_host,
stdout=PIPE,