Add support for hermetic remote builds #20

Merged
MatthewCroughan merged 1 commit from mc/hermetic-remote into master 2022-05-30 18:53:20 +00:00
3 changed files with 62 additions and 11 deletions
Showing only changes of commit 4a356409ca - Show all commits

View file

@ -32,6 +32,7 @@ Below is a minimal example:
host = "itchy.scratchy.com";
sshUser = "matthew";
buildOn = "remote"; # valid args are "local" or "remote"
hermetic = false;
};
}
# ... other configuration ...
@ -76,3 +77,30 @@ reloading user units for matthew...
setting up tmpfiles
Connection to itchy.scratchy.com closed.
```
# Available arguments via `_module.args.nixinate`
- `host` *`string`*
A string representing the hostname or IP address of a machine to connect to
via ssh.
- `sshUser` *`string`*
A string representing the username a machine to connect to via ssh.
- `buildOn` *`"remote"`* or *`"local"`*
- `"remote"`
Push the flake to the remote, build and activate entirely remotely,
returning logs via SSH.
- `"local"`
Build the system closure locally, copy to the remote and activate.
- `hermetic` *`bool`*
Whether to copy Nix to the remote for usage when building and activating,
instead of using the Nix which is already installed on the remote.

View file

@ -16,32 +16,49 @@
{
herculesCI.ciSystems = [ "x86_64-linux" ];
overlay = final: prev: {
nixinate = {
nix = prev.pkgs.writeShellScriptBin "nix"
''${final.nixVersions.unstable}/bin/nix --experimental-features "nix-command flakes" "$@"'';
Kranzes commented 2022-05-29 17:04:22 +00:00 (Migrated from github.com)
Review

You should avoid using Unstable here. Since 2.4 flakes are supported.

You should avoid using Unstable here. Since 2.4 flakes are supported.
MatthewCroughan commented 2022-05-29 17:31:36 +00:00 (Migrated from github.com)
Review

Evaluation time is getting better with Nix releases, so I'd rather use unstable, since 2.4 is pretty slow. Also, there are some determinism issues with lockfile generation that I'd prefer to avoid, by using the latest.

Evaluation time is getting better with Nix releases, so I'd rather use unstable, since 2.4 is pretty slow. Also, there are some determinism issues with lockfile generation that I'd prefer to avoid, by using the latest.
nixos-rebuild = prev.nixos-rebuild.override { inherit (final) nix; };
};
generateApps = flake:
let
machines = builtins.attrNames flake.nixosConfigurations;
validMachines = final.lib.remove "" (final.lib.forEach machines (x: final.lib.optionalString (flake.nixosConfigurations."${x}"._module.args ? nixinate) "${x}" ));
mkDeployScript = { machine, dryRun }: let
inherit (builtins) abort;
inherit (final.lib) getExe;
nix = "${getExe final.nix}";
nixos-rebuild = "${getExe final.nixos-rebuild}";
openssh = "${getExe final.openssh}";
n = flake.nixosConfigurations.${machine}._module.args.nixinate;
hermetic = n.hermetic or false;
user = n.sshUser or "root";
host = n.host;
where = n.buildOn or "remote";
remote = if where == "remote" then true else if where == "local" then false else abort "_module.args.nixinate.buildOn is not set to a valid value of 'local' or 'remote'";
switch = if dryRun then "dry-activate" else "switch";
script = ''
script =
''
set -e
echo "🚀 Deploying nixosConfigurations.${machine} from ${flake}"
echo "👤 SSH User: ${user}"
echo "🌐 SSH Host: ${host}"
'' + (if remote then ''
echo "🚀 Sending flake to ${machine} via nix copy:"
( set -x; ${final.nix}/bin/nix copy ${flake} --to ssh://${user}@${host} )
echo "🤞 Activating configuration on ${machine} via ssh:"
( set -x; ${final.openssh}/bin/ssh -t ${user}@${host} 'sudo nixos-rebuild ${switch} --flake ${flake}#${machine}' )
( set -x; ${nix} copy ${flake} --to ssh://${user}@${host} )
'' + (if hermetic then ''
echo "🤞 Activating configuration hermetically on ${machine} via ssh:"
( set -x; ${nix} build ${nixos-rebuild} --no-link --store ssh://${user}@${host} )
( set -x; ${openssh} -t ${user}@${host} 'sudo ${nixos-rebuild} ${switch} --flake ${flake}#${machine}' )
'' else ''
echo "🤞 Activating configuration non-hermetically on ${machine} via ssh:"
( set -x; ${openssh} -t ${user}@${host} 'sudo nixos-rebuild ${switch} --flake ${flake}#${machine}' )
'')
else ''
echo "🔨 Building system closure locally, copying it to remote store and activating it:"
( set -x; NIX_SSHOPTS="-t" ${final.nixos-rebuild}/bin/nixos-rebuild ${switch} --flake ${flake}#${machine} --target-host ${user}@${host} --use-remote-sudo )
( set -x; NIX_SSHOPTS="-t" ${nixos-rebuild} ${switch} --flake ${flake}#${machine} --target-host ${user}@${host} --use-remote-sudo )
'');
in final.writeScript "deploy-${machine}.sh" script;
in
@ -78,7 +95,7 @@
let
vmTests = import ./tests {
makeTest = (import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system; }).makeTest;
inherit pkgs inputs;
inherit inputs; pkgs = nixpkgsFor.${system};
};
in
pkgs.lib.optionalAttrs pkgs.stdenv.isLinux vmTests # vmTests can only be ran on Linux, so append them only if on Linux.

View file

@ -1,5 +1,6 @@
{ pkgs, makeTest, inputs }:
let
inherit (pkgs) lib;
# Return a store path with a closure containing everything including
# derivations and all build dependency outputs, all the way down.
allDrvOutputs = pkg:
@ -22,7 +23,7 @@ let
((import (path + "/flake.nix")).outputs (inputs // {self = r;}));
in
r;
mkNixinateTest = buildOn:
mkNixinateTest = { buildOn, hermetic ? false, ... }:
let
exampleFlake = pkgs.writeTextFile {
name = "nixinate-example-flake";
@ -41,6 +42,7 @@ let
host = "nixinatee";
sshUser = "nixinator";
buildOn = "${buildOn}"; # valid args are "local" or "remote"
hermetic = ${lib.boolToString hermetic}; # valid args are true or false
};
}
];
@ -66,7 +68,9 @@ let
];
virtualisation = {
writableStore = true;
additionalPaths = [] ++ pkgs.lib.optional (buildOn == "remote") (allDrvOutputs exampleSystem);
additionalPaths = []
++ lib.optional (buildOn == "remote") (allDrvOutputs exampleSystem)
++ lib.optional (hermetic == true) (pkgs.nixinate.nixos-rebuild);
};
};
nixinator = { ... }: {
@ -77,7 +81,7 @@ let
additionalPaths = [
(allDrvOutputs exampleSystem)
]
++ pkgs.lib.optional (buildOn == "remote") exampleFlake;
++ lib.optional (buildOn == "remote") exampleFlake;
};
};
};
@ -103,6 +107,8 @@ let
};
in
{
local = (mkNixinateTest "local");
remote = (mkNixinateTest "remote");
local = (mkNixinateTest { buildOn = "local"; });
remote = (mkNixinateTest { buildOn = "remote"; });
localHermetic = (mkNixinateTest { buildOn = "local"; hermetic = true; });
remoteHermetic = (mkNixinateTest { buildOn = "remote"; hermetic = true; });
}