stockholm/krebs/default.nix

283 lines
7.5 KiB
Nix

{ current-host-name
, current-user-name
, lib
, stockholm
, StrictHostKeyChecking ? "yes"
}:
let out = {
inherit deploy;
inherit infest;
inherit init;
inherit nixos-install;
inherit populate;
};
deploy =
{ system ? current-host-name
, target ? system
}@args: let
config = get-config system;
in ''
#! /bin/sh
# krebs.deploy
set -efu
(${populate args})
${rootssh target ''
${nix-install args}
${config.krebs.build.profile}/bin/switch-to-configuration switch
''}
echo OK
'';
infest =
{ system ? current-host-name
, target ? system
}@args: let
config = get-config system;
in ''
#! /bin/sh
# krebs.infest
set -efu
${rootssh target ''
${builtins.readFile ./4lib/infest/prepare.sh}
${builtins.readFile ./4lib/infest/install-nix.sh}
''}
# Prepare target source via bind-mounting
(${nixos-install args})
${rootssh target ''
${builtins.readFile ./4lib/infest/finalize.sh}
''}
'';
init =
{ system ? current-host-name
}@args: let
config = get-config system;
in ''
#! /bin/sh
# krebs.init
set -efu
system=${lib.shell.escape system}
secrets_dir=${config.krebs.build.source.dir.secrets.path}
key_type=ed25519
key_file=$secrets_dir/ssh.id_$key_type
key_comment=$system
if test -e "$key_file"; then
echo "Warning: privkey already exists: $key_file" >&2
else
ssh-keygen \
-C "$key_comment" \
-t "$key_type" \
-f "$key_file" \
-N ""
rm "$key_file.pub"
fi
pubkey=$(ssh-keygen -y -f "$key_file")
cat<<EOF
# put following into config.krebs.hosts.$system:
ssh.privkey.path = <secrets/ssh.$key_type>;
ssh.pubkey = $(echo $pubkey | jq -R .);
EOF
'';
nixos-install =
{ system ? current-host-name
, target ? system
}@args: let
in ''
#! /bin/sh
# krebs.nixos-install
(${populate (args // { root = "/mnt"; })})
${rootssh target ''
export PATH; PATH=/root/.nix-profile/bin:$PATH
src=$(type -p nixos-install)
cat_src() {
sed < "$src" "$(
{ sed < "$src" -n '
/^if ! test -e "\$mountPoint\/\$NIXOS_CONFIG/,/^fi$/=
/^nixpkgs=/=
/^NIX_PATH=/,/^$/{/./=}
# Disable: Copy the NixOS/Nixpkgs sources to the target as
# the initial contents of the NixOS channel.
/^srcs=/,/^ln -sfn /=
'
} | sed 's:$:s/^/#krebs#/:'
)"
}
# Location to insert `nix-install`
i=$(sed -n '/^echo "building the system configuration/=' "$src")
{
cat_src | sed -n "1,$i{p}"
cat ${doc (nix-install args)}
cat_src | sed -n "$i,\''${$i!p}"
} > nixos-install
chmod +x nixos-install
# Wrap inserted nix-install into chroot.
nix_env=$(cat_src | sed -n '
s:.*\(/nix/store/[a-z0-9]*-nix-[0-9.]\+/bin/nix-env\).*:\1:p;T;q
')
echo "nix-env is $nix_env" >&2
findpkg() {(
name=$1
path=$(find /nix/store \
-mindepth 1 -maxdepth 1 -type d -name '*-'"$name"'-*' \
| head -n 1 | sed s:^/mnt::)
if echo "$path" | grep .; then
echo "$name is $path" >&2
else
echo "Error: package not found: $name" >&2
exit 1
fi
)}
cacert=$(findpkg cacert)
coreutils=$(findpkg coreutils)
cp "$cacert"/etc/ssl/certs/ca-bundle.crt /mnt/root/SSL_CERT_FILE
env="$coreutils/bin/env SSL_CERT_FILE=/root/SSL_CERT_FILE"
sed -i '
s:^NIX_PATH=:chroot $mountPoint '"$env"' &:
s:^nix-env:'"$nix_env"':
' nixos-install
./nixos-install
''}
'';
doc = s:
let b = "EOF${builtins.hashString "sha256" s}"; in
''
<<\${b}
${s}
${b}
'';
get-config = system: let
config = stockholm.users.${current-user-name}.${system}.config
or (abort "unknown system: ${system}, user: ${current-user-name}");
in
assert config.krebs.build.source-version == 1;
config;
nix-install =
{ system ? current-host-name
, target ? system
}:
let
config = get-config system;
nix-path =
lib.concatStringsSep ":"
(lib.mapAttrsToList (name: src: "${name}=${src.target-path}")
(config.krebs.build.source.dir //
config.krebs.build.source.git));
in ''
set -efu
NIX_PATH=${lib.shell.escape nix-path} \
nix-env \
--show-trace \
-f '<stockholm>' \
--argstr current-host-name ${lib.shell.escape current-host-name} \
--argstr current-user-name ${lib.shell.escape current-user-name} \
--profile ${lib.shell.escape config.krebs.build.profile} \
--set \
-A ${lib.escapeShellArg (lib.concatStringsSep "." [
"users"
config.krebs.build.user.name
config.krebs.build.host.name
"system"
])}
'';
populate =
{ system ? current-host-name
, target ? system
, root ? ""
}@args:
let out = ''
#! /bin/sh
set -efu
${lib.concatStringsSep "\n"
(lib.concatMap
(type: lib.mapAttrsToList (_: methods.${type})
config.krebs.build.source.${type})
["dir" "git"])}
'';
config = get-config system;
current-host = config.krebs.hosts.${current-host-name};
current-user = config.krebs.users.${current-user-name};
methods.dir = config:
let
can-push = config.host.name == current-host.name;
target-path = root + config.target-path;
push-method = ''
rsync \
--exclude .git \
--exclude .graveyard \
--exclude old \
--exclude tmp \
--rsync-path='mkdir -p ${target-path} && rsync' \
--delete-excluded \
-vrlptD \
${config.path}/ \
root@${target}:${target-path}
'';
in
if can-push then push-method else
let dir = "file://${config.host.name}${config.path}"; in
# /!\ revise this message when using more than just push-method
throw "No way to push ${dir} from ${current-host.name} to ${target}";
methods.git = config:
let target-path = root + config.target-path;
in rootssh target ''
mkdir -p ${target-path}
cd ${target-path}
if ! test -e .git; then
git init
fi
if ! cur_url=$(git config remote.origin.url 2>/dev/null); then
git remote add origin ${config.url}
elif test "$cur_url" != ${config.url}; then
git remote set-url origin ${config.url}
fi
if test "$(git rev-parse --verify HEAD 2>/dev/null)" != ${config.rev}; then
git fetch origin
git checkout ${config.rev} -- .
git checkout -q ${config.rev}
git submodule init
git submodule update
fi
git clean -dxf
'';
in out;
rootssh = target: script:
let
flags = "-o StrictHostKeyChecking=${StrictHostKeyChecking}";
in
"ssh ${flags} root@${target} -T ${doc ''
set -efu
${script}
''}";
in out