mirror of
https://github.com/nix-community/nixos-anywhere.git
synced 2024-08-16 20:00:36 +03:00
commit
8db4c0a233
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
result
|
49
flake.lock
Normal file
49
flake.lock
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"nodes": {
|
||||
"disko": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1670529818,
|
||||
"narHash": "sha256-flPaFV2V4SBtGY+u5CYJzQWJutd1D6zSwomILcSgtCI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "disko",
|
||||
"rev": "66add2cd9edab96f5e87477229772bf2b3ca513a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "master",
|
||||
"repo": "disko",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1670559856,
|
||||
"narHash": "sha256-xUkgQRFqE6HIFQXs9SIXMZiXcLaH2415UR6w/FnsgcY=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6bc6f77cb171a74001033d94f17f49043a9f1804",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"disko": "disko",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
35
flake.nix
Normal file
35
flake.nix
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
description = "A universal nixos installer, just needs ssh access to the target system";
|
||||
|
||||
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
inputs.disko.url = "github:nix-community/disko/master";
|
||||
inputs.disko.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
outputs = { self, disko, nixpkgs, ... }: let
|
||||
supportedSystems = [
|
||||
"x86_64-linux"
|
||||
"i686-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
in {
|
||||
packages = forAllSystems (system: let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in {
|
||||
nixos-remote = pkgs.callPackage ./package.nix {};
|
||||
default = self.packages.${system}.nixos-remote;
|
||||
});
|
||||
checks.x86_64-linux = let
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
in {
|
||||
from-nixos = import ./tests/from-nixos.nix {
|
||||
inherit pkgs;
|
||||
disko = disko.nixosModules.disko;
|
||||
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
|
||||
eval-config = import (pkgs.path + "/nixos/lib/eval-config.nix");
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
79
nixos-remote
79
nixos-remote
@ -1,21 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eufo pipefail
|
||||
set -x
|
||||
|
||||
showUsage() {
|
||||
cat <<USAGE
|
||||
Usage: $0 [options] ssh-host
|
||||
Usage: nixos-remote [options] ssh-host
|
||||
|
||||
Options:
|
||||
|
||||
* -f, --flake flake
|
||||
set the flake to install the system from
|
||||
* --arg name value
|
||||
pass value to nix-build. can be used to set disk-names for example
|
||||
* --argstr name value
|
||||
pass value to nix-build as string
|
||||
* -s, --store-paths
|
||||
set the store paths to the disko-script and nixos-system directly
|
||||
if this is give, flake is not needed
|
||||
* --no-ssh-copy
|
||||
skip copying ssh-keys to target system
|
||||
* --kexec url
|
||||
use another kexec tarball to bootstrap NixOS
|
||||
* --debug
|
||||
enable debug output
|
||||
USAGE
|
||||
}
|
||||
|
||||
@ -24,8 +26,8 @@ abort() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
nix_args=()
|
||||
kexec_url=https://github.com/nix-community/nixos-images/releases/download/nixos-22.05/nixos-kexec-installer-x86_64-linux.tar.gz
|
||||
kexec_url=https://github.com/nix-community/nixos-images/releases/download/nixos-22.11/nixos-kexec-installer-x86_64-linux.tar.gz
|
||||
debug=n
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
@ -33,8 +35,9 @@ while [[ $# -gt 0 ]]; do
|
||||
flake=$2
|
||||
shift
|
||||
;;
|
||||
--argstr | --arg)
|
||||
nix_args+=("$1" "$2" "$3")
|
||||
-s | --store-paths)
|
||||
disko_script=$2
|
||||
nixos_system=$3
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
@ -49,9 +52,17 @@ while [[ $# -gt 0 ]]; do
|
||||
--no-ssh-copy-id)
|
||||
no_ssh_copy=y
|
||||
;;
|
||||
--debug)
|
||||
debug=y
|
||||
set -x
|
||||
;;
|
||||
*)
|
||||
if [ -z ${ssh_connection+x} ]; then
|
||||
ssh_connection=$1
|
||||
if [[ "$1" =~ ^ssh:// ]]; then
|
||||
ssh_connection=$1
|
||||
else
|
||||
ssh_connection="ssh://$1"
|
||||
fi
|
||||
else
|
||||
showUsage
|
||||
exit 1
|
||||
@ -61,6 +72,7 @@ while [[ $# -gt 0 ]]; do
|
||||
shift
|
||||
done
|
||||
|
||||
|
||||
# ssh wrapper
|
||||
timeout_ssh_() {
|
||||
timeout 10 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$ssh_connection" "$@"
|
||||
@ -80,20 +92,29 @@ nix_build() {
|
||||
"$@"
|
||||
}
|
||||
|
||||
nixos_system=""
|
||||
if [ -z ${ssh_connection+x} ]; then
|
||||
abort "ssh-host must be set"
|
||||
fi
|
||||
|
||||
# parse flake nixos-install style syntax, get the system attr
|
||||
if [[ ! -z "${flake+x}" ]]; then
|
||||
if [[ -n "${flake+x}" ]]; then
|
||||
if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
|
||||
flake="${BASH_REMATCH[1]}"
|
||||
flakeAttr="${BASH_REMATCH[2]}"
|
||||
fi
|
||||
if [[ -z "$flakeAttr" ]]; then
|
||||
if [[ -z "${flakeAttr+x}" ]]; then
|
||||
echo "Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri."
|
||||
echo "For example, to use the output nixosConfigurations.foo from the flake.nix, append \"#foo\" to the flake-uri."
|
||||
exit 1
|
||||
fi
|
||||
disko_script=$(nix_build "${flake}#nixosConfigurations.${flakeAttr}.config.system.build.disko")
|
||||
nixos_system=$(nix_build "${flake}#nixosConfigurations.${flakeAttr}.config.system.build.toplevel")
|
||||
elif [[ -n "${disko_script+x}" ]] && [[ -n "${nixos_system+x}" ]]; then
|
||||
if [[ ! -e "${disko_script}" ]] || [[ ! -e "${nixos_system}" ]]; then
|
||||
echo "${disko_script} and ${nixos_system} must be existing store-paths"
|
||||
exit 1
|
||||
fi
|
||||
:
|
||||
else
|
||||
abort "flake must be set"
|
||||
fi
|
||||
@ -101,20 +122,23 @@ fi
|
||||
|
||||
# wait for machine to become reachable (possibly forever)
|
||||
if [ ${no_ssh_copy-n} != "y" ]; then
|
||||
until ssh-copy-id "$ssh_connection"; do sleep 5; done
|
||||
until ssh-copy-id -o ConnectTimeout=10 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$ssh_connection"; do sleep 5; done
|
||||
else
|
||||
until ssh_ -o ConnectTimeout=10 -- exit 0; do sleep 5; done
|
||||
fi
|
||||
|
||||
# first check if the remote system is kexec booted
|
||||
if $(ssh_ -- test -e /etc/is_kexec); then
|
||||
if ssh_ -- test -e /etc/is_kexec; then
|
||||
is_kexec=y
|
||||
fi
|
||||
|
||||
if [ ${is_kexec-n} != "y" ]; then
|
||||
# TODO we probably need an architecture detection here
|
||||
ssh_ << SSH
|
||||
set -efux
|
||||
set -efu
|
||||
if [ "$debug" = "y" ]; then
|
||||
set -x
|
||||
fi
|
||||
os=\$(uname)
|
||||
if [[ "\$os" != "Linux" ]]; then
|
||||
echo "This script requires Linux as the operating system, but got \${os}" >&2
|
||||
@ -129,17 +153,26 @@ rm -rf /root/kexec
|
||||
mkdir -p /root/kexec
|
||||
SSH
|
||||
|
||||
if [[ -e "$kexec_url" ]]; then
|
||||
cat "$kexec_url" | ssh_ 'tar -C /root/kexec -xvzf-'
|
||||
if [[ -f "$kexec_url" ]]; then
|
||||
ssh_ 'tar -C /root/kexec -xvzf-' < "$kexec_url"
|
||||
else
|
||||
ssh_ << SSH
|
||||
set -eu -o pipefail
|
||||
if [ "$debug" = "y" ]; then
|
||||
set -x
|
||||
fi
|
||||
fetch(){
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl --fail -Ss -L "\$1"
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget "\$1" -O-
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y curl
|
||||
curl --fail -Ss -L "\$1"
|
||||
else
|
||||
echo "no downloader (curl or wget) found, bailing out"
|
||||
echo "no downloader (curl or wget) found, bailing out" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
@ -159,10 +192,10 @@ SSH
|
||||
until ssh_ -o ConnectTimeout=10 -- exit 0; do sleep 5; done
|
||||
fi
|
||||
|
||||
nixCopy --to "ssh://$ssh_connection" "$disko_script"
|
||||
ssh_ $disko_script
|
||||
nixCopy --to "$ssh_connection" "$disko_script"
|
||||
ssh_ "$disko_script"
|
||||
|
||||
nixCopy --to "ssh://$ssh_connection?remote-store=local?root=/mnt" "$nixos_system"
|
||||
nixCopy --to "$ssh_connection?remote-store=local?root=/mnt" "$nixos_system"
|
||||
ssh_ << SSH
|
||||
set -efu
|
||||
nixos-install --no-root-passwd --no-channel-copy --system "$nixos_system"
|
||||
|
35
package.nix
Normal file
35
package.nix
Normal file
@ -0,0 +1,35 @@
|
||||
{ stdenvNoCC
|
||||
, makeWrapper
|
||||
, lib
|
||||
, openssh
|
||||
, gitMinimal
|
||||
, nix
|
||||
, coreutils
|
||||
, shellcheck
|
||||
}:
|
||||
let
|
||||
runtimeDeps = [
|
||||
openssh
|
||||
gitMinimal # for git flakes
|
||||
nix
|
||||
coreutils
|
||||
];
|
||||
in
|
||||
stdenvNoCC.mkDerivation {
|
||||
name = "nixos-remote";
|
||||
src = ./.;
|
||||
nativeBuildInputs = [
|
||||
makeWrapper
|
||||
shellcheck
|
||||
];
|
||||
installPhase = ''
|
||||
install -D -m755 nixos-remote $out/bin/nixos-remote
|
||||
wrapProgram "$out/bin/nixos-remote" \
|
||||
--prefix PATH : "${lib.makeBinPath runtimeDeps}"
|
||||
'';
|
||||
|
||||
doCheck = true;
|
||||
checkPhase = ''
|
||||
shellcheck ./nixos-remote
|
||||
'';
|
||||
}
|
129
tests/from-nixos.nix
Normal file
129
tests/from-nixos.nix
Normal file
@ -0,0 +1,129 @@
|
||||
{ pkgs ? import <nixpkgs> {}
|
||||
, makeTest ? import <nixpkgs/nixos/tests/make-test-python.nix>
|
||||
, eval-config ? import <nixpkgs/nixos/lib/eval-config.nix>
|
||||
, disko ? "${builtins.fetchTarball "https://github.com/nix-community/disko/archive/master.tar.gz"}/module.nix"
|
||||
, ... }:
|
||||
let
|
||||
kexec_tarball = builtins.fetchurl {
|
||||
url = "https://github.com/nix-community/nixos-images/releases/download/nixos-22.05/nixos-kexec-installer-x86_64-linux.tar.gz";
|
||||
sha256 = "sha256:1ff7rc0n8w1vab7k9sir3029sizfaq8yx7y8r1id94gcqrr2n1sd";
|
||||
};
|
||||
systemToInstall = { modulesPath, ... }: {
|
||||
imports = [
|
||||
disko
|
||||
(modulesPath + "/testing/test-instrumentation.nix")
|
||||
(modulesPath + "/profiles/qemu-guest.nix")
|
||||
(modulesPath + "/profiles/minimal.nix")
|
||||
];
|
||||
networking.hostName = "nixos-remote";
|
||||
documentation.enable = false;
|
||||
hardware.enableAllFirmware = false;
|
||||
networking.hostId = "8425e349"; # from profiles/base.nix, needed for zfs
|
||||
boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms
|
||||
boot.loader.grub.devices = [ "/dev/vda" ];
|
||||
disko.devices = {
|
||||
disk = {
|
||||
vda = {
|
||||
device = "/dev/vda";
|
||||
type = "disk";
|
||||
content = {
|
||||
type = "table";
|
||||
format = "gpt";
|
||||
partitions = [
|
||||
{
|
||||
name = "boot";
|
||||
type = "partition";
|
||||
start = "0";
|
||||
end = "1M";
|
||||
part-type = "primary";
|
||||
flags = ["bios_grub"];
|
||||
}
|
||||
{
|
||||
type = "partition";
|
||||
name = "ESP";
|
||||
start = "1MiB";
|
||||
end = "100MiB";
|
||||
bootable = true;
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "vfat";
|
||||
mountpoint = "/boot";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "root";
|
||||
type = "partition";
|
||||
start = "100MiB";
|
||||
end = "100%";
|
||||
part-type = "primary";
|
||||
bootable = true;
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "ext4";
|
||||
mountpoint = "/";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
evaledSystem = eval-config {
|
||||
modules = [ systemToInstall ];
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
in
|
||||
makeTest {
|
||||
name = "nixos-remote";
|
||||
nodes = {
|
||||
installer = {
|
||||
documentation.enable = false;
|
||||
environment.etc.sshKey = {
|
||||
source = ./ssh-keys/ssh;
|
||||
mode = "0600";
|
||||
};
|
||||
programs.ssh.startAgent = true;
|
||||
system.extraDependencies = [
|
||||
evaledSystem.config.system.build.disko
|
||||
evaledSystem.config.system.build.toplevel
|
||||
];
|
||||
};
|
||||
installed = {
|
||||
virtualisation.memorySize = 4096;
|
||||
documentation.enable = false;
|
||||
services.openssh.enable = true;
|
||||
users.users.root.openssh.authorizedKeys.keyFiles = [ ./ssh-keys/ssh.pub ];
|
||||
};
|
||||
};
|
||||
testScript = ''
|
||||
def create_test_machine(oldmachine=None, args={}): # taken from <nixpkgs/nixos/tests/installer.nix>
|
||||
machine = create_machine({
|
||||
"qemuFlags":
|
||||
'-cpu max -m 1024 -virtfs local,path=/nix/store,security_model=none,mount_tag=nix-store,'
|
||||
f' -drive file={oldmachine.state_dir}/installed.qcow2,id=drive1,if=none,index=1,werror=report'
|
||||
f' -device virtio-blk-pci,drive=drive1',
|
||||
} | args)
|
||||
driver.machines.append(machine)
|
||||
return machine
|
||||
|
||||
start_all()
|
||||
installed.wait_for_unit("sshd.service")
|
||||
installer.succeed("""
|
||||
eval $(ssh-agent)
|
||||
ssh-add /etc/sshKey
|
||||
${../nixos-remote} \
|
||||
--no-ssh-copy-id \
|
||||
--kexec ${kexec_tarball} \
|
||||
--store-paths ${toString evaledSystem.config.system.build.disko} ${toString evaledSystem.config.system.build.toplevel} \
|
||||
root@installed >&2
|
||||
""")
|
||||
installed.shutdown()
|
||||
new_machine = create_test_machine(oldmachine=installed, args={ "name": "after_install" })
|
||||
new_machine.start()
|
||||
assert "nixos-remote" == new_machine.succeed("hostname").strip()
|
||||
'';
|
||||
} {
|
||||
pkgs = pkgs;
|
||||
system = pkgs.system;
|
||||
}
|
7
tests/ssh-keys/ssh
Normal file
7
tests/ssh-keys/ssh
Normal file
@ -0,0 +1,7 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACDM4kPg4V7DMYceuA8VIUdBvw30gM9qagERA8v1VGBBBQAAAJDpULAq6VCw
|
||||
KgAAAAtzc2gtZWQyNTUxOQAAACDM4kPg4V7DMYceuA8VIUdBvw30gM9qagERA8v1VGBBBQ
|
||||
AAAECpjfl5WMMIDvEyZJTeXzRNFzpDpj4fqdIXHZauKAlE5MziQ+DhXsMxhx64DxUhR0G/
|
||||
DfSAz2pqAREDy/VUYEEFAAAACWxhc3NAbW9ycwECAwQ=
|
||||
-----END OPENSSH PRIVATE KEY-----
|
1
tests/ssh-keys/ssh.pub
Normal file
1
tests/ssh-keys/ssh.pub
Normal file
@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMziQ+DhXsMxhx64DxUhR0G/DfSAz2pqAREDy/VUYEEF
|
Loading…
Reference in New Issue
Block a user