wfvm: reorganize, add demo-ssh

This commit is contained in:
Sebastien Bourdeauducq 2020-06-15 00:28:31 +08:00
parent 18f84a65c6
commit 4510ae7552
6 changed files with 145 additions and 98 deletions

View File

@ -3,4 +3,5 @@
{
makeWindowsImage = attrs: import ./win.nix ({ inherit pkgs; } // attrs);
layers = (import ./layers { inherit pkgs; });
utils = (import ./utils.nix { inherit pkgs; });
}

View File

@ -1,9 +1,11 @@
{ pkgs ? import <nixpkgs> {}, impureMode ? false }:
let
win = (import ./default.nix { inherit pkgs; });
wfvm = (import ./default.nix { inherit pkgs; });
in
win.makeWindowsImage {
wfvm.makeWindowsImage {
# Build install script & skip building iso
inherit impureMode;
# Custom base iso
# windowsImage = pkgs.fetchurl {
@ -11,6 +13,10 @@ win.makeWindowsImage {
# sha256 = "668fe1af70c2f7416328aee3a0bb066b12dc6bbd2576f40f812b95741e18bc3a";
# };
# impureShellCommands = [
# "powershell.exe echo Hello"
# ];
# User accounts
users = {
artiq = {
@ -23,23 +29,15 @@ win.makeWindowsImage {
};
};
# Build install script & skip building iso
inherit impureMode;
# impureShellCommands = [
# "powershell.exe echo Hello"
# ];
fullName = "M-Labs";
organization = "m-labs";
administratorPassword = "12345";
# Auto login
defaultUser = "artiq";
fullName = "M-Labs";
organization = "m-labs";
administratorPassword = "12345";
# Imperative installation commands, to be installed incrementally
installCommands = with win.layers; [ anaconda3 msys2 msys2-packages ];
installCommands = with wfvm.layers; [ anaconda3 msys2 msys2-packages ];
# services = {
# # Enable remote management

View File

@ -0,0 +1,11 @@
{ pkgs ? import <nixpkgs> {} }:
let
wfvm = (import ./default.nix { inherit pkgs; });
in
wfvm.utils.wfvm-run {
name = "demo-ssh";
image = import ./demo-image.nix { inherit pkgs; };
display = true;
script = "${pkgs.openssh}/bin/ssh -p 2022 wfvm@localhost";
}

View File

@ -10,7 +10,7 @@
};
in ''
ln -s ${Anaconda3} ./Anaconda3.exe
win-put Anaconda3.exe 'C:\Users\artiq'
win-put Anaconda3.exe 'C:\Users\wfvm'
echo Running Anaconda installer...
win-exec 'start /wait "" .\Anaconda3.exe /S /D=%UserProfile%\Anaconda3'
echo Anaconda installer finished
@ -32,8 +32,8 @@
in ''
ln -s ${msys2} ./msys2.exe
ln -s ${msys2-auto-install} ./auto-install.js
win-put msys2.exe 'C:\Users\artiq'
win-put auto-install.js 'C:\Users\artiq'
win-put msys2.exe 'C:\Users\wfvm'
win-put auto-install.js 'C:\Users\wfvm'
echo Running MSYS2 installer...
# work around MSYS2 installer bug that prevents it from closing at the end of unattended install
expect -c 'set timeout 600; spawn win-exec ".\\msys2.exe --script auto-install.js -v InstallPrefix=C:\\msys64"; expect FinishedPageCallback { close }'
@ -45,7 +45,7 @@
script = let
msys-packages = import ./msys_packages.nix { inherit pkgs; };
msys-packages-put = pkgs.lib.strings.concatStringsSep "\n"
(map (package: ''win-put ${package} 'C:\Users\artiq\msyspackages' '') msys-packages);
(map (package: ''win-put ${package} 'C:\Users\wfvm\msyspackages' '') msys-packages);
in
# Windows command line is so shitty it can't even do glob expansion. Why do people use Windows?
''
@ -55,9 +55,9 @@
set MSYS=c:\msys64
set ARCH=64
set PATH=%MSYS%\usr\bin;%MSYS%\mingw%ARCH%\bin;%PATH%
bash -c "pacman -U --noconfirm C:/Users/artiq/msyspackages/*"
bash -c "pacman -U --noconfirm C:/Users/wfvm/msyspackages/*"
EOF
win-put installmsyspackages.bat 'C:\Users\artiq'
win-put installmsyspackages.bat 'C:\Users\wfvm'
win-exec installmsyspackages
'';
};

92
artiq-fast/wfvm/utils.nix Normal file
View File

@ -0,0 +1,92 @@
{ pkgs, baseRtc ? "2020-04-20T14:21:42", cores ? "4", qemuMem ? "4G" }:
rec {
# qemu_test is a smaller closure only building for a single system arch
qemu = pkgs.qemu_test;
mkQemuFlags = extraFlags: [
"-enable-kvm"
"-cpu host"
"-smp ${cores}"
"-m ${qemuMem}"
"-bios ${pkgs.OVMF.fd}/FV/OVMF.fd"
"-vga virtio"
"-rtc base=${baseRtc}"
"-device piix3-usb-uhci"
"-device e1000,netdev=n1"
] ++ extraFlags;
# Pass empty config file to prevent ssh from failing to create ~/.ssh
sshOpts = "-F /dev/null -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=\$TMP/known_hosts -o ConnectTimeout=1";
win-exec = pkgs.writeShellScriptBin "win-exec" ''
${pkgs.sshpass}/bin/sshpass -p1234 -- \
${pkgs.openssh}/bin/ssh -np 2022 ${sshOpts} \
wfvm@localhost \
$1
'';
win-wait = pkgs.writeShellScriptBin "win-wait" ''
# If the machine is not up within 10 minutes it's likely never coming up
timeout=600
# Wait for VM to be accessible
sleep 20
echo "Waiting for SSH..."
while true; do
if test "$timeout" -eq 0; then
echo "SSH connection timed out"
exit 1
fi
output=$(${win-exec}/bin/win-exec 'echo|set /p="Ran command"' || echo "")
if test "$output" = "Ran command"; then
break
fi
echo "Retrying in 1 second, timing out in $timeout seconds"
((timeout=$timeout-1))
sleep 1
done
echo "SSH OK"
'';
win-put = pkgs.writeShellScriptBin "win-put" ''
echo scp windows $1 -\> $2
${pkgs.sshpass}/bin/sshpass -p1234 -- \
${pkgs.openssh}/bin/scp -P 2022 ${sshOpts} \
$1 wfvm@localhost:$2
'';
wfvm-run = { name, image, script, display ? false, isolateNetwork ? true, forwardedPorts ? [] }:
let
restrict =
if isolateNetwork
then "on"
else "off";
# use socat instead of `tcp:...` to allow multiple connections
guestfwds =
builtins.concatStringsSep ""
(map ({ listenAddr, targetAddr, port }:
",guestfwd=tcp:${listenAddr}:${toString port}-cmd:${pkgs.socat}/bin/socat\\ -\\ tcp:${targetAddr}:${toString port}"
) forwardedPorts);
qemuParams = mkQemuFlags (pkgs.lib.optional (!display) "-nographic" ++ [
"-drive"
"file=${image},index=0,media=disk,cache=unsafe"
"-snapshot"
"-netdev user,id=n1,net=192.168.1.0/24,restrict=${restrict},hostfwd=tcp::2022-:22${guestfwds}"
]);
in pkgs.writeShellScriptBin "wfvm-run-${name}" ''
set -m
qemu-system-x86_64 ${pkgs.lib.concatStringsSep " " qemuParams} &
${win-wait}/bin/win-wait
${script}
echo "Shutting down..."
${win-exec}/bin/win-exec 'shutdown /s'
echo "Waiting for VM to terminate..."
fg
echo "Done"
'';
}

View File

@ -1,10 +1,8 @@
{ pkgs
, diskImageSize ? "22G"
, qemuMem ? "4G"
, windowsImage ? null
, autoUnattendParams ? {}
, impureMode ? false
, baseRtc ? "2020-04-20T14:21:42"
, installCommands ? []
, users ? {}
, ...
@ -12,8 +10,7 @@
let
lib = pkgs.lib;
# qemu_test is a smaller closure only building for a single system arch
qemu = pkgs.qemu_test;
utils = import ./utils.nix { inherit pkgs; };
libguestfs = pkgs.libguestfs-with-appliance;
# p7zip on >20.03 has known vulns but we have no better option
@ -24,7 +21,7 @@ let
});
runQemuCommand = name: command: (
pkgs.runCommandNoCC name { buildInputs = [ p7zip qemu libguestfs ]; }
pkgs.runCommandNoCC name { buildInputs = [ p7zip utils.qemu libguestfs ]; }
(
''
if ! test -f; then
@ -45,6 +42,15 @@ let
autounattend = import ./autounattend.nix (
attrs // {
inherit pkgs;
users = users // {
wfvm = {
password = "1234";
description = "WFVM Administrator";
groups = [
"Administrators"
];
};
};
}
);
@ -66,27 +72,9 @@ let
virt-make-fs --partition --type=fat pkgs/ $out
'';
mkQemuFlags = extraFlags: [
"-enable-kvm"
"-cpu"
"host"
"-smp"
"$NIX_BUILD_CORES"
"-m"
"${qemuMem}"
"-bios"
"${pkgs.OVMF.fd}/FV/OVMF.fd"
"-vga"
"virtio"
"-device"
"piix3-usb-uhci" # USB root hub
"-rtc base=${baseRtc}"
"-device e1000,netdev=n1"
] ++ lib.optional (!impureMode) "-nographic" ++ extraFlags;
installScript = pkgs.writeScript "windows-install-script" (
let
qemuParams = mkQemuFlags [
qemuParams = utils.mkQemuFlags (lib.optional (!impureMode) "-nographic" ++ [
# "CD" drive with bootstrap pkgs
"-drive"
"id=virtio-win,file=${bootstrapPkgs},if=none,format=raw,readonly=on"
@ -102,16 +90,12 @@ let
"file=c.img,index=0,media=disk,cache=unsafe"
# Network
"-netdev user,id=n1,net=192.168.1.0/24,restrict=on"
];
]);
in
''
#!${pkgs.runtimeShell}
set -euxo pipefail
export PATH=${lib.makeBinPath [ p7zip qemu libguestfs ]}:$PATH
if test -z "''${NIX_BUILD_CORES+x}"; then
export NIX_BUILD_CORES=$(nproc)
fi
export PATH=${lib.makeBinPath [ p7zip utils.qemu libguestfs ]}:$PATH
# Create a bootable "USB" image
# Booting in USB mode circumvents the "press any key to boot from cdrom" prompt
@ -128,7 +112,7 @@ let
# Qemu requires files to be rw
qemu-img create -f qcow2 c.img ${diskImageSize}
env NIX_BUILD_CORES="''${NIX_BUILD_CORES:4}" qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams}
qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams}
''
);
@ -137,36 +121,19 @@ let
mv c.img $out
'';
# Pass empty config file to prevent ssh from failing to create ~/.ssh
sshOpts = "-F /dev/null -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=\$TMP/known_hosts -o ConnectTimeout=1";
win-exec = pkgs.writeShellScriptBin "win-exec" ''
${pkgs.sshpass}/bin/sshpass -p${users.artiq.password} -- \
${pkgs.openssh}/bin/ssh -np 2022 ${sshOpts} \
artiq@localhost \
$1
'';
win-put = pkgs.writeShellScriptBin "win-put" ''
echo scp windows $1 -\> $2
${pkgs.sshpass}/bin/sshpass -p${users.artiq.password} -- \
${pkgs.openssh}/bin/scp -P 2022 ${sshOpts} \
$1 artiq@localhost:$2
'';
finalImage = builtins.foldl' (acc: v: pkgs.runCommandNoCC "${v.name}.img" {
buildInputs = [
win-exec
win-put
qemu
buildInputs = with utils; [
qemu win-wait win-exec win-put
] ++ (v.buildInputs or []);
} (let
script = pkgs.writeScript "${v.name}-script" v.script;
qemuParams = mkQemuFlags [
qemuParams = utils.mkQemuFlags (lib.optional (!impureMode) "-nographic" ++ [
# Output image
"-drive"
"file=c.img,index=0,media=disk,cache=unsafe"
# Network - enable SSH forwarding
"-netdev user,id=n1,net=192.168.1.0/24,restrict=on,hostfwd=tcp::2022-:22"
];
]);
in ''
# Create an image referencing the previous image in the chain
@ -175,33 +142,11 @@ let
set -m
qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams} &
# If the machine is not up within 10 minutes it's likely never coming up
timeout=600
win-wait
# Wait for VM to be accessible
sleep 20
echo "Waiting for SSH"
while true; do
if test "$timeout" -eq 0; then
echo "SSH connection timed out"
exit 1
fi
output=$(win-exec 'echo|set /p="Ran command"' || echo "")
if test "$output" = "Ran command"; then
break
fi
echo "Retrying in 1 second, timing out in $timeout seconds"
((timeout=$timeout-1))
sleep 1
done
echo "Executing user script..."
echo "Executing script to build layer..."
${script}
echo "User script done"
echo "Layer script done"
echo "Shutting down..."
win-exec 'shutdown /s'