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); makeWindowsImage = attrs: import ./win.nix ({ inherit pkgs; } // attrs);
layers = (import ./layers { inherit pkgs; }); layers = (import ./layers { inherit pkgs; });
utils = (import ./utils.nix { inherit pkgs; });
} }

View File

@ -1,9 +1,11 @@
{ pkgs ? import <nixpkgs> {}, impureMode ? false }: { pkgs ? import <nixpkgs> {}, impureMode ? false }:
let let
win = (import ./default.nix { inherit pkgs; }); wfvm = (import ./default.nix { inherit pkgs; });
in in
win.makeWindowsImage { wfvm.makeWindowsImage {
# Build install script & skip building iso
inherit impureMode;
# Custom base iso # Custom base iso
# windowsImage = pkgs.fetchurl { # windowsImage = pkgs.fetchurl {
@ -11,6 +13,10 @@ win.makeWindowsImage {
# sha256 = "668fe1af70c2f7416328aee3a0bb066b12dc6bbd2576f40f812b95741e18bc3a"; # sha256 = "668fe1af70c2f7416328aee3a0bb066b12dc6bbd2576f40f812b95741e18bc3a";
# }; # };
# impureShellCommands = [
# "powershell.exe echo Hello"
# ];
# User accounts # User accounts
users = { users = {
artiq = { 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 # Auto login
defaultUser = "artiq"; defaultUser = "artiq";
fullName = "M-Labs";
organization = "m-labs";
administratorPassword = "12345";
# Imperative installation commands, to be installed incrementally # Imperative installation commands, to be installed incrementally
installCommands = with win.layers; [ anaconda3 msys2 msys2-packages ]; installCommands = with wfvm.layers; [ anaconda3 msys2 msys2-packages ];
# services = { # services = {
# # Enable remote management # # 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 '' in ''
ln -s ${Anaconda3} ./Anaconda3.exe ln -s ${Anaconda3} ./Anaconda3.exe
win-put Anaconda3.exe 'C:\Users\artiq' win-put Anaconda3.exe 'C:\Users\wfvm'
echo Running Anaconda installer... echo Running Anaconda installer...
win-exec 'start /wait "" .\Anaconda3.exe /S /D=%UserProfile%\Anaconda3' win-exec 'start /wait "" .\Anaconda3.exe /S /D=%UserProfile%\Anaconda3'
echo Anaconda installer finished echo Anaconda installer finished
@ -32,8 +32,8 @@
in '' in ''
ln -s ${msys2} ./msys2.exe ln -s ${msys2} ./msys2.exe
ln -s ${msys2-auto-install} ./auto-install.js ln -s ${msys2-auto-install} ./auto-install.js
win-put msys2.exe 'C:\Users\artiq' win-put msys2.exe 'C:\Users\wfvm'
win-put auto-install.js 'C:\Users\artiq' win-put auto-install.js 'C:\Users\wfvm'
echo Running MSYS2 installer... echo Running MSYS2 installer...
# work around MSYS2 installer bug that prevents it from closing at the end of unattended install # 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 }' 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 script = let
msys-packages = import ./msys_packages.nix { inherit pkgs; }; msys-packages = import ./msys_packages.nix { inherit pkgs; };
msys-packages-put = pkgs.lib.strings.concatStringsSep "\n" 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 in
# Windows command line is so shitty it can't even do glob expansion. Why do people use Windows? # 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 MSYS=c:\msys64
set ARCH=64 set ARCH=64
set PATH=%MSYS%\usr\bin;%MSYS%\mingw%ARCH%\bin;%PATH% 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 EOF
win-put installmsyspackages.bat 'C:\Users\artiq' win-put installmsyspackages.bat 'C:\Users\wfvm'
win-exec installmsyspackages 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 { pkgs
, diskImageSize ? "22G" , diskImageSize ? "22G"
, qemuMem ? "4G"
, windowsImage ? null , windowsImage ? null
, autoUnattendParams ? {} , autoUnattendParams ? {}
, impureMode ? false , impureMode ? false
, baseRtc ? "2020-04-20T14:21:42"
, installCommands ? [] , installCommands ? []
, users ? {} , users ? {}
, ... , ...
@ -12,8 +10,7 @@
let let
lib = pkgs.lib; lib = pkgs.lib;
# qemu_test is a smaller closure only building for a single system arch utils = import ./utils.nix { inherit pkgs; };
qemu = pkgs.qemu_test;
libguestfs = pkgs.libguestfs-with-appliance; libguestfs = pkgs.libguestfs-with-appliance;
# p7zip on >20.03 has known vulns but we have no better option # p7zip on >20.03 has known vulns but we have no better option
@ -24,7 +21,7 @@ let
}); });
runQemuCommand = name: command: ( runQemuCommand = name: command: (
pkgs.runCommandNoCC name { buildInputs = [ p7zip qemu libguestfs ]; } pkgs.runCommandNoCC name { buildInputs = [ p7zip utils.qemu libguestfs ]; }
( (
'' ''
if ! test -f; then if ! test -f; then
@ -45,6 +42,15 @@ let
autounattend = import ./autounattend.nix ( autounattend = import ./autounattend.nix (
attrs // { attrs // {
inherit pkgs; 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 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" ( installScript = pkgs.writeScript "windows-install-script" (
let let
qemuParams = mkQemuFlags [ qemuParams = utils.mkQemuFlags (lib.optional (!impureMode) "-nographic" ++ [
# "CD" drive with bootstrap pkgs # "CD" drive with bootstrap pkgs
"-drive" "-drive"
"id=virtio-win,file=${bootstrapPkgs},if=none,format=raw,readonly=on" "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" "file=c.img,index=0,media=disk,cache=unsafe"
# Network # Network
"-netdev user,id=n1,net=192.168.1.0/24,restrict=on" "-netdev user,id=n1,net=192.168.1.0/24,restrict=on"
]; ]);
in in
'' ''
#!${pkgs.runtimeShell} #!${pkgs.runtimeShell}
set -euxo pipefail set -euxo pipefail
export PATH=${lib.makeBinPath [ p7zip qemu libguestfs ]}:$PATH export PATH=${lib.makeBinPath [ p7zip utils.qemu libguestfs ]}:$PATH
if test -z "''${NIX_BUILD_CORES+x}"; then
export NIX_BUILD_CORES=$(nproc)
fi
# Create a bootable "USB" image # Create a bootable "USB" image
# Booting in USB mode circumvents the "press any key to boot from cdrom" prompt # 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 requires files to be rw
qemu-img create -f qcow2 c.img ${diskImageSize} 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 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" { finalImage = builtins.foldl' (acc: v: pkgs.runCommandNoCC "${v.name}.img" {
buildInputs = [ buildInputs = with utils; [
win-exec qemu win-wait win-exec win-put
win-put
qemu
] ++ (v.buildInputs or []); ] ++ (v.buildInputs or []);
} (let } (let
script = pkgs.writeScript "${v.name}-script" v.script; script = pkgs.writeScript "${v.name}-script" v.script;
qemuParams = mkQemuFlags [ qemuParams = utils.mkQemuFlags (lib.optional (!impureMode) "-nographic" ++ [
# Output image # Output image
"-drive" "-drive"
"file=c.img,index=0,media=disk,cache=unsafe" "file=c.img,index=0,media=disk,cache=unsafe"
# Network - enable SSH forwarding # Network - enable SSH forwarding
"-netdev user,id=n1,net=192.168.1.0/24,restrict=on,hostfwd=tcp::2022-:22" "-netdev user,id=n1,net=192.168.1.0/24,restrict=on,hostfwd=tcp::2022-:22"
]; ]);
in '' in ''
# Create an image referencing the previous image in the chain # Create an image referencing the previous image in the chain
@ -175,33 +142,11 @@ let
set -m set -m
qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams} & qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams} &
# If the machine is not up within 10 minutes it's likely never coming up win-wait
timeout=600
# Wait for VM to be accessible echo "Executing script to build layer..."
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..."
${script} ${script}
echo "User script done" echo "Layer script done"
echo "Shutting down..." echo "Shutting down..."
win-exec 'shutdown /s' win-exec 'shutdown /s'