forked from M-Labs/nix-scripts
Add support for incremental install of packages
This commit is contained in:
parent
7b4cd0b944
commit
28da250925
@ -14,7 +14,7 @@
|
|||||||
, timeZone ? "UTC"
|
, timeZone ? "UTC"
|
||||||
, services ? {}
|
, services ? {}
|
||||||
, impureShellCommands ? []
|
, impureShellCommands ? []
|
||||||
, driveLetter ? "F:"
|
, driveLetter ? "E:"
|
||||||
, ...
|
, ...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
|
@ -42,6 +42,26 @@ win.makeWindowsImage {
|
|||||||
# Auto login
|
# Auto login
|
||||||
defaultUser = "artiq";
|
defaultUser = "artiq";
|
||||||
|
|
||||||
|
# Imperative installation commands, to be installed incrementally
|
||||||
|
installCommands = [
|
||||||
|
|
||||||
|
{
|
||||||
|
name = "Anaconda3";
|
||||||
|
script = let
|
||||||
|
Anaconda3 = pkgs.fetchurl {
|
||||||
|
name = "Anaconda3.exe";
|
||||||
|
url = "https://repo.anaconda.com/archive/Anaconda3-2019.03-Windows-x86_64.exe";
|
||||||
|
sha256 = "1f9icm5rwab6l1f23a70dw0qixzrl62wbglimip82h4zhxlh3jfj";
|
||||||
|
};
|
||||||
|
in ''
|
||||||
|
cp ${Anaconda3} ./Anaconda3.exe
|
||||||
|
win put Anaconda3.exe 'C:\Users\artiq'
|
||||||
|
win exec 'start /wait "" .\Anaconda3.exe /S /D=%UserProfile%\Anaconda3'
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
# services = {
|
# services = {
|
||||||
# # Enable remote management
|
# # Enable remote management
|
||||||
# WinRm = {
|
# WinRm = {
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
, packages ? []
|
, packages ? []
|
||||||
, impureMode ? false
|
, impureMode ? false
|
||||||
, baseRtc ? "2020-04-20T14:21:42"
|
, baseRtc ? "2020-04-20T14:21:42"
|
||||||
|
, installCommands ? []
|
||||||
|
, users ? {}
|
||||||
, ...
|
, ...
|
||||||
}@attrs:
|
}@attrs:
|
||||||
|
|
||||||
@ -103,6 +105,7 @@ let
|
|||||||
cp ${bundleInstaller} pkgs/"$(stripHash "${bundleInstaller}")"
|
cp ${bundleInstaller} pkgs/"$(stripHash "${bundleInstaller}")"
|
||||||
|
|
||||||
# Install optional windows features
|
# Install optional windows features
|
||||||
|
|
||||||
cp ${openSshServerPackage} pkgs/fod/OpenSSH-Server-Package~31bf3856ad364e35~amd64~~.cab
|
cp ${openSshServerPackage} pkgs/fod/OpenSSH-Server-Package~31bf3856ad364e35~amd64~~.cab
|
||||||
|
|
||||||
# SSH setup script goes here because windows XML parser sucks
|
# SSH setup script goes here because windows XML parser sucks
|
||||||
@ -131,6 +134,10 @@ let
|
|||||||
# "-cdrom" "${fodIso}"
|
# "-cdrom" "${fodIso}"
|
||||||
# Set the base clock inside the VM
|
# Set the base clock inside the VM
|
||||||
"-rtc base=${baseRtc}"
|
"-rtc base=${baseRtc}"
|
||||||
|
# Always enable SSH port forward
|
||||||
|
# It's not really required for the initial setup but we do it here anyway
|
||||||
|
"-netdev user,id=n1,net=192.168.1.0/24,restrict=off,hostfwd=tcp::2022-:22"
|
||||||
|
"-device e1000,netdev=n1"
|
||||||
] ++ lib.optional (!impureMode) "-nographic" ++ extraFlags;
|
] ++ lib.optional (!impureMode) "-nographic" ++ extraFlags;
|
||||||
|
|
||||||
installScript = pkgs.writeScript "windows-install-script" (
|
installScript = pkgs.writeScript "windows-install-script" (
|
||||||
@ -179,8 +186,116 @@ let
|
|||||||
''
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
|
baseImage = pkgs.runCommandNoCC "windows.img" {} ''
|
||||||
|
${installScript}
|
||||||
|
mv c.img $out
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Use Paramiko instead of OpenSSH
|
||||||
|
#
|
||||||
|
# OpenSSH goes out of it's way to make password logins hard
|
||||||
|
# and Windows goes out of it's way to make key authentication hard
|
||||||
|
# so we're in a pretty tough spot
|
||||||
|
#
|
||||||
|
# Luckily the usage patterns are quite simple and easy to reimplement with paramiko
|
||||||
|
paramikoClient = pkgs.writeScriptBin "win" ''
|
||||||
|
#!${pkgs.python3.withPackages(ps: [ ps.paramiko ])}/bin/python
|
||||||
|
import paramiko
|
||||||
|
import os.path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def w_join(*args):
|
||||||
|
# Like os.path.join but for windows paths
|
||||||
|
return "\\".join(args)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
client = paramiko.SSHClient()
|
||||||
|
client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy)
|
||||||
|
|
||||||
|
|
||||||
|
cmd = sys.argv[1]
|
||||||
|
|
||||||
|
try:
|
||||||
|
client.connect(hostname="127.0.0.1", port=2022, username="artiq", password="${users.artiq.password}", timeout=1)
|
||||||
|
|
||||||
|
if cmd == "put":
|
||||||
|
sftp = client.open_sftp()
|
||||||
|
src = sys.argv[2]
|
||||||
|
dst = sys.argv[3]
|
||||||
|
sftp.put(src, w_join(dst, os.path.basename(src)))
|
||||||
|
|
||||||
|
elif cmd == "exec":
|
||||||
|
_, stdout, stderr = client.exec_command(sys.argv[2])
|
||||||
|
|
||||||
|
sys.stdout.write(stdout.read().strip().decode())
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
sys.stderr.write(stderr.read().strip().decode())
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unhandled command: {cmd}")
|
||||||
|
except (EOFError, paramiko.ssh_exception.SSHException):
|
||||||
|
exit(1)
|
||||||
|
'';
|
||||||
|
|
||||||
|
finalImage = builtins.foldl' (acc: v: pkgs.runCommandNoCC "${v.name}.img" {
|
||||||
|
buildInputs = [
|
||||||
|
paramikoClient
|
||||||
|
qemu
|
||||||
|
];
|
||||||
|
} (let
|
||||||
|
script = pkgs.writeScript "${v.name}-script" v.script;
|
||||||
|
qemuParams = mkQemuFlags [
|
||||||
|
# Output image
|
||||||
|
"-drive"
|
||||||
|
"file=c.img,index=0,media=disk,cache=unsafe"
|
||||||
|
];
|
||||||
|
|
||||||
|
in ''
|
||||||
|
export HOME=$(mktemp -d)
|
||||||
|
|
||||||
|
# Create an image referencing the previous image in the chain
|
||||||
|
qemu-img create -f qcow2 -b ${acc} c.img
|
||||||
|
|
||||||
|
qemu-system-x86_64 ${lib.concatStringsSep " " qemuParams} &
|
||||||
|
|
||||||
|
# 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 'echo 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 to build layer"
|
||||||
|
|
||||||
|
${script}
|
||||||
|
|
||||||
|
win exec 'shutdown /s'
|
||||||
|
|
||||||
|
mv c.img $out
|
||||||
|
'')) baseImage installCommands;
|
||||||
|
|
||||||
in
|
in
|
||||||
if impureMode then installScript else pkgs.runCommandNoCC "windows.img" {} ''
|
|
||||||
${installScript}
|
# impureMode is meant for debugging the base image, not the full incremental build process
|
||||||
mv c.img $out
|
if !(impureMode) then finalImage else assert installCommands == []; installScript
|
||||||
''
|
|
||||||
|
Loading…
Reference in New Issue
Block a user