Skip to content

Instantly share code, notes, and snippets.

@lovesegfault
Created August 14, 2020 23:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lovesegfault/36130da3ef78054aba146f797eb696bb to your computer and use it in GitHub Desktop.
Save lovesegfault/36130da3ef78054aba146f797eb696bb to your computer and use it in GitHub Desktop.
#!/nix/store/z4ajipns0l1s8b2lrgpy6nng4cys7h99-bash-4.4-p23/bin/bash
PATH=/nix/store/z4ajipns0l1s8b2lrgpy6nng4cys7h99-bash-4.4-p23/bin:/nix/store/hzvl3dvv8651iqlb5g6gq5hzjzmhjn7m-coreutils-8.31/bin:/nix/store/rphcpivxfm4blw36ki262yrmqygw9pcp-findutils-4.7.0/bin:/nix/store/9k90a16frvqdcxzkhxcjsi2qczv3i4x4-gnused-4.8/bin:/nix/store/zckg7fafpkpmdhgajyalakirlw88k1jz-jq-1.6-bin/bin:/nix/store/83f38s2vp9nlkh3akilbhw2869xy0b9l-nix-2.3.3/bin:/nix/store/ban4jcddacl71ld8axbnlnlh5gr0712q-openssh-8.2p1/bin:/nix/store/nz8baf2s4wygv1nc6pybb7dc5wn3yw8f-procps-3.3.16/bin:/nix/store/308x65jfhxxybjwp46492y6zqqw2z2b8-rsync-3.1.3/bin
set -euo pipefail
# Kill all child processes when interrupting/exiting
trap exit INT TERM
trap 'for pid in $(jobs -p) ; do kill -- -$pid ; done' EXIT
# Be sure to use --foreground for all timeouts, therwise a Ctrl-C won't stop them!
# See https://unix.stackexchange.com/a/233685/214651
# Prefix all output with host name
# From https://unix.stackexchange.com/a/440439/214651
exec > >(sed "s/^/[fourier] /")
exec 2> >(sed "s/^/[fourier] /" >&2)
HOST=10.0.0.10
echo "Connecting to host..." >&2
if ! OLDSYSTEM=$(timeout --foreground 30 \
ssh -o ControlPath=none -o BatchMode=yes "$HOST" realpath /run/current-system\
); then
echo "Unable to connect to host!" >&2
exit 1
fi
if [ "$OLDSYSTEM" == "/nix/store/hawq17bn59yif121cq71x115ambykpas-nixos-system-fourier-20.09pre-git" ]; then
echo "No deploy necessary" >&2
#exit 0
fi
# ======== PHASE: copy-closure ========
echo "Copying closure to host..." >&2
# TOOD: Prevent garbage collection until the end of the deploy
tries=3
while [ "$tries" -ne 0 ] &&
! NIX_SSH_OPTS="-o ServerAliveInterval=15" nix-copy-closure --to "$HOST" '/nix/store/hawq17bn59yif121cq71x115ambykpas-nixos-system-fourier-20.09pre-git' '/nix/store/xyrzxnwhnw3gx2hr1lqy0jgb8nja0f2r-switch'; do
tries=$(( $tries - 1 ))
echo "Failed to copy closure, $tries tries left"
done
# ======== PHASE: secrets ========
echo "Copying secrets..." >&2
ssh "$HOST" sudo mkdir -p -m 755 /var/lib/nixus-secrets/pending/per-{user,group}
# TODO: I don't think this works if rsync isn't on the remote's shell.
# We really just need a single binary we can execute on the remote, like the switch script
rsync --perms --chmod=440 --rsync-path="sudo rsync" "/nix/store/28c9aghrlqgsrk3nf2qipcqzklqgp127-required-secrets" "$HOST:/var/lib/nixus-secrets/pending/included-secrets"
while read -r json; do
name=$(echo "$json" | jq -r '.name')
source=$(echo "$json" | jq -r '.source')
user=$(echo "$json" | jq -r '.user')
group=$(echo "$json" | jq -r '.group')
echo "Copying secret $name..." >&2
# If this is a per-user secret
if [[ "$user" != null ]]; then
# The -n is very important for ssh to not swallow stdin!
ssh -n "$HOST" sudo mkdir -p -m 500 "/var/lib/nixus-secrets/pending/per-user/$user"
rsync --perms --chmod=400 --rsync-path="sudo rsync" "$source" "$HOST:/var/lib/nixus-secrets/pending/per-user/$user/$name"
else
ssh -n "$HOST" sudo mkdir -p -m 050 "/var/lib/nixus-secrets/pending/per-group/$group"
rsync --perms --chmod=040 --rsync-path="sudo rsync" "$source" "$HOST:/var/lib/nixus-secrets/pending/per-group/$group/$name"
fi
done < "/nix/store/28c9aghrlqgsrk3nf2qipcqzklqgp127-required-secrets"
echo "Finished copying secrets" >&2
# ======== PHASE: switch ========
echo "Triggering system switcher..." >&2
id=$(ssh -o BatchMode=yes "$HOST" exec "/nix/store/xyrzxnwhnw3gx2hr1lqy0jgb8nja0f2r-switch/bin/switch" start "/nix/store/hawq17bn59yif121cq71x115ambykpas-nixos-system-fourier-20.09pre-git")
echo "Trying to confirm success..." >&2
active=1
while [ "$active" != 0 ]; do
# TODO: Because of the imperative network-setup script, when e.g. the
# defaultGateway is removed, the previous entry is still persisted on
# a rebuild switch, even though with a reboot it wouldn't. Maybe use
# the more modern and declarative networkd to get around this
set +e
status=$(timeout --foreground 15 ssh -o ControlPath=none -o BatchMode=yes "$HOST" exec "/nix/store/xyrzxnwhnw3gx2hr1lqy0jgb8nja0f2r-switch/bin/switch" active "$id")
active=$?
set -e
sleep 1
done
case "$status" in
"success")
echo "Successfully activated new system!" >&2
;;
"failure")
echo "Failed to activate new system! Rolled back to previous one" >&2
echo "Run the following command to see the logs for the switch:" >&2
echo "ssh ${HOST@Q} sudo cat /var/lib/system-switcher/system-$id/log" >&2
# TODO: Try to better show what failed
;;
*)
echo "This shouldn't occur, the status is $status!" >&2
;;
esac
echo "Finished" >&2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment