Skip to content

Commit

Permalink
Let's go!
Browse files Browse the repository at this point in the history
  • Loading branch information
blaggacao committed Aug 13, 2021
1 parent 37f033d commit f8012da
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 12 deletions.
44 changes: 44 additions & 0 deletions chroot-wrapper.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
set -e

# Re-exec ourselves in a private mount namespace so that our bind
# mounts get cleaned up automatically.
if [ -z "$NIXOS_ENTER_REEXEC" ]; then
export NIXOS_ENTER_REEXEC=1
if [ "$(id -u)" != 0 ]; then
extraFlags="-r"
fi
exec unshare --fork --mount --uts --mount-proc --pid $extraFlags -- "$0" "$@"
else
mount --make-rprivate /
fi

mountPoint=/mnt

while [ "$#" -gt 0 ]; do
i="$1"; shift 1
case "$i" in
--root)
mountPoint="$1"; shift 1
;;
--)
command=("$@")
break
;;
*)
echo "$0: unknown option \`$i'"
exit 1
;;
esac
done

mkdir -p "$mountPoint/dev" "$mountPoint/sys" "$mountPoint/tmp" "$mountPoint/etc" "$mountPoint/proc"
chmod 0755 "$mountPoint/dev" "$mountPoint/sys" "$mountPoint/tmp" "$mountPoint/etc" "$mountPoint/proc"
mount --rbind /dev "$mountPoint/dev"
mount --rbind /sys "$mountPoint/sys"
mount --rbind /proc "$mountPoint/proc"

touch "$mountPoint/etc/mtab" || true # might be already a working system
(cd "$mountPoint" && ln -snf "../proc/self/mounts" "etc/mtab") # Grub needs an mtab.

export CHROOTED=1
exec chroot "$mountPoint" "${command[@]}"
49 changes: 38 additions & 11 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,22 @@
executable = true;
destination = "/deploy-rs-activate";
})
(final.writeTextFile {
name = base.name + "-activate-chroot-wrapper";
text = ''
# no interpreter: absolute storepaths are not resolvable outside chroot
# run with bash <>, instead
${nixpkgs.lib.fileContents ./chroot-wrapper.bash};
'';
executable = true;
destination = "/deploy-rs-chroot-wrapper";
})
(final.writeTextFile {
name = base.name + "-activate-rs";
text = ''
#!${final.runtimeShell}
# required by bin/activate
export PATH="${nixpkgs.lib.makeBinPath [final.nixUnstable final.coreutils]}"
exec ${self.defaultPackage.${system}}/bin/activate "$@"
'';
executable = true;
Expand All @@ -84,34 +96,49 @@
};

nixos = base: (custom // { dryActivate = "$PROFILE/bin/switch-to-configuration dry-activate"; }) base.config.system.build.toplevel ''
export PATH="${nixpkgs.lib.makeBinPath [final.coreutils]}"
# work around https://github.com/NixOS/nixpkgs/issues/73404
cd /tmp
_SYSTEM="/nix/var/nix/profiles/system"
_SWITCH_COMMAND="$PROFILE/bin/switch-to-configuration switch" # always relative to root
_SWITCH_COMMAND="$PROFILE/bin/switch-to-configuration"
# Set $LOCALE_ARCHIVE to supress some Perl locale warnings.
export LOCALE_ARCHIVE="$PROFILE/sw/lib/locale/locale-archive"
_already_on_nixos() { [[ -f "/etc/NIXOS" ]]; }
_ensure_fs_contract() { mkdir -m 0755 -p /etc; touch /etc/NIXOS; }
_insall_bootloader_and_switch() {
ln -sfn /proc/mounts /etc/mtab # Grub needs an mtab.
NIXOS_INSTALL_BOOTLOADER=1 $_SWITCH_COMMAND
_switch_cmd() {
"$_SWITCH_COMMAND" "$1"
}
_switch_configuration() {
$_SWITCH_COMMAND
_become_a_nixos() { touch /etc/NIXOS; }
_if_chrooted() { [[ "''${CHROOTED:-}" == "1" ]] && echo $@; }
_activate_system() {
# Run the activation script.
"$PROFILE/activate" 1>&2 || true
# Cooperatively respect our chrooted environment
${final.systemd}/bin/systemd-tmpfiles --create --remove $(_if_chrooted "--exclude-prefix=/dev") 1>&2 || true;
}
if _already_on_nixos
then
_switch_configuration
_switch_cmd switch
else
_ensure_fs_contract
_insall_bootloader_and_switch
echo "-> Become a NixOS ..."
_become_a_nixos
echo "-> Activate the system ..."
_activate_system
echo "-> Install the boot loader ..."
NIXOS_INSTALL_BOOTLOADER=1 _switch_cmd boot
echo "-> Reboot ..."
$(_if_chrooted sync && echo b |tee /proc/sysrq-trigger)
${final.systemd}/bin/reboot --reboot
fi
# https://github.com/serokell/deploy-rs/issues/31
${with base.config.boot.loader;
final.lib.optionalString systemd-boot.enable
"sed -i '/^default /d' ${efi.efiSysMountPoint}/loader/loader.conf"}
"${final.gnused}/bin/sed -i '/^default /d' ${efi.efiSysMountPoint}/loader/loader.conf"}
'';

home-manager = base: custom base.activationPackage "$PROFILE/activate";
Expand Down
27 changes: 26 additions & 1 deletion src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ impl<'a> ActivateCommand<'a> {
fn build(self) -> String {
let mut cmd = format!("{}/activate-rs", self.closure);

// Exec trough our chroot wrapper that lives within the profile
// down there on the mounted filesystem.
if let Some(mount_point) = self.mount_point {
cmd = format!("bash {0}$(readlink -m {0}{1}/deploy-rs-chroot-wrapper) --root {0} -- {2}", mount_point, self.closure, cmd);
}

if self.debug_logs {
cmd = format!("{} --debug-logs", cmd);
}
Expand Down Expand Up @@ -158,6 +164,12 @@ impl<'a> WaitCommand<'a> {
fn build(self) -> String {
let mut cmd = format!("{}/activate-rs", self.closure);

// Exec trough our chroot wrapper that lives within the profile
// down there on the mounted filesystem.
if let Some(mount_point) = self.mount_point {
cmd = format!("bash {0}$(readlink -m {0}{1}/deploy-rs-chroot-wrapper) --root {0} -- {2}", mount_point, self.closure, cmd);
}

if self.debug_logs {
cmd = format!("{} --debug-logs", cmd);
}
Expand Down Expand Up @@ -226,6 +238,12 @@ impl<'a> RevokeCommand<'a> {
fn build(self) -> String {
let mut cmd = format!("{}/activate-rs", self.closure);

// Exec trough our chroot wrapper that lives within the profile
// down there on the mounted filesystem.
if let Some(mount_point) = self.mount_point {
cmd = format!("bash {0}$(readlink -m {0}{1}/deploy-rs-chroot-wrapper) --root {0} -- {2}", mount_point, self.closure, cmd);
}

if self.debug_logs {
cmd = format!("{} --debug-logs", cmd);
}
Expand Down Expand Up @@ -267,6 +285,7 @@ fn test_revoke_command_builder() {
}

pub struct ConfirmCommand<'a> {
mount_point: Option<&'a str>,
sudo: Option<&'a str>,
temp_path: &'a str,
closure: &'a str,
Expand All @@ -275,6 +294,7 @@ pub struct ConfirmCommand<'a> {
impl<'a> ConfirmCommand<'a> {
pub fn from_data(d: &'a data::DeployData) -> Self {
ConfirmCommand {
mount_point: d.flags.mount_point.as_deref(),
sudo: d.sudo.as_deref(),
temp_path: &d.temp_path,
closure: &d.profile.profile_settings.path,
Expand All @@ -285,7 +305,12 @@ impl<'a> ConfirmCommand<'a> {
fn build(self) -> String {
let lock_path = super::make_lock_path(&self.temp_path, &self.closure);

let mut cmd = format!("rm {}", lock_path);
let mut cmd;
if let Some(mount_point) = self.mount_point {
cmd = format!("rm {}{}", mount_point, lock_path);
} else {
cmd = format!("rm {}", lock_path);
}
if let Some(sudo_cmd) = &self.sudo {
cmd = format!("{} {}", sudo_cmd, cmd);
}
Expand Down
7 changes: 7 additions & 0 deletions src/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub enum PushProfileError {
#[error("Activation script activate-rs does not exist in profile.\n\
Is there a mismatch in deploy-rs used in the flake you're deploying and deploy-rs command you're running?")]
ActivateRsDoesntExist,
#[error("Activation script helper deploy-rs-chroot-helper does not exist in profile.\n\
Is there a mismatch in deploy-rs used in the flake you're deploying and deploy-rs command you're running?")]
DeployRsChrootWrapperDoesntExist,
#[error("Failed to run Nix sign command: {0}")]
SignError(std::io::Error),
#[error("Nix sign command resulted in a bad exit code: {0:?}")]
Expand Down Expand Up @@ -249,6 +252,10 @@ pub async fn push_profile(
return Err(PushProfileError::DeployRsActivateDoesntExist);
}

if !Path::new(format!("{}/deploy-rs-chroot-wrapper", closure).as_str()).exists() {
return Err(PushProfileError::DeployRsChrootWrapperDoesntExist);
}

if !Path::new(format!("{}/activate-rs", closure).as_str()).exists() {
return Err(PushProfileError::ActivateRsDoesntExist);
}
Expand Down

0 comments on commit f8012da

Please sign in to comment.