Add presets support

This commit is contained in:
Roey Darwish Dror
2019-06-05 10:05:59 +03:00
parent 91215703bf
commit ffbd34e14f
7 changed files with 150 additions and 19 deletions

View File

@@ -41,6 +41,10 @@ pub struct CreateCommand {
/// Encrypt the root partition
#[structopt(short = "e", long = "encrypted-root")]
pub encrypted_root: bool,
/// Path to preset files
#[structopt(long = "presets")]
pub presets: Vec<PathBuf>,
}
#[derive(StructOpt)]

View File

@@ -6,7 +6,7 @@ pub struct Error {
inner: Context<ErrorKind>,
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
#[fail(display = "Error quering information about the block device")]
DeviceQuery,
@@ -73,6 +73,12 @@ pub enum ErrorKind {
#[fail(display = "Failed launching Qemu")]
Qemu,
#[fail(display = "Error loading preset \"{}\"", _0)]
Preset(String),
#[fail(display = "Error executing preset script")]
PresetScript,
}
impl Fail for Error {

View File

@@ -1,5 +1,6 @@
mod args;
mod error;
mod presets;
mod process;
mod storage;
mod tool;
@@ -13,9 +14,10 @@ use failure::{Fail, ResultExt};
use log::{debug, error, info, warn};
use nix::sys::signal;
use simplelog::*;
use std::collections::HashSet;
use std::fs;
use std::io::Write;
use std::os::unix::process::CommandExt as UnixCommandExt;
use std::os::unix::{fs::PermissionsExt, process::CommandExt as UnixCommandExt};
use std::path::Path;
use std::process::exit;
use std::thread;
@@ -74,6 +76,8 @@ fn fix_fstab(fstab: &str) -> String {
}
fn create(command: CreateCommand) -> Result<(), Error> {
let presets = presets::Presets::load(&command.presets)?;
let sgdisk = Tool::find("sgdisk")?;
let pacstrap = Tool::find("pacstrap")?;
let arch_chroot = Tool::find("arch-chroot")?;
@@ -141,19 +145,26 @@ fn create(command: CreateCommand) -> Result<(), Error> {
let mount_stack = mount(mount_point.path(), &boot_filesystem, &root_filesystem)?;
let mut packages: HashSet<String> = [
"base",
"grub",
"efibootmgr",
"intel-ucode",
"networkmanager",
"broadcom-wl",
]
.iter()
.map(|s| String::from(*s))
.collect();
packages.extend(presets.packages);
info!("Bootstrapping system");
pacstrap
.execute()
.arg("-c")
.arg(mount_point.path())
.args(&[
"base",
"grub",
"efibootmgr",
"intel-ucode",
"networkmanager",
"broadcom-wl",
])
.args(packages)
.args(&command.extra_packages)
.run(ErrorKind::Pacstrap)?;
@@ -167,6 +178,33 @@ fn create(command: CreateCommand) -> Result<(), Error> {
debug!("fstab:\n{}", fstab);
fs::write(mount_point.path().join("etc/fstab"), fstab).context(ErrorKind::Fstab)?;
if !presets.scripts.is_empty() {
info!("Running custom scripts");
}
for script in presets.scripts {
let mut script_file =
tempfile::NamedTempFile::new_in(mount_point.path()).context(ErrorKind::PresetScript)?;
script_file
.write_all(script.as_bytes())
.and_then(|_| script_file.as_file_mut().metadata())
.and_then(|metadata| {
let mut permissions = metadata.permissions();
permissions.set_mode(0o755);
fs::set_permissions(script_file.path(), permissions)
})
.context(ErrorKind::PresetScript)?;
let script_path = script_file.into_temp_path();
arch_chroot
.execute()
.arg(mount_point.path())
.arg(Path::new("/").join(script_path.file_name().unwrap()))
.run(ErrorKind::PostInstallation)?;
}
info!("Performing post installation tasks");
arch_chroot
.execute()
.arg(mount_point.path())

50
src/presets.rs Normal file
View File

@@ -0,0 +1,50 @@
use crate::error::{Error, ErrorKind};
use failure::ResultExt;
use serde::Deserialize;
use std::collections::HashSet;
use std::fs;
use std::path::{Path, PathBuf};
use toml;
#[derive(Deserialize)]
struct Preset {
packages: Option<Vec<String>>,
script: Option<String>,
}
impl Preset {
fn load(path: &Path) -> Result<Self, Error> {
let data = fs::read_to_string(path)
.with_context(|_| ErrorKind::Preset(format!("{}", path.display())))?;
Ok(toml::from_str(&data)
.with_context(|_| ErrorKind::Preset(format!("{}", path.display())))?)
}
}
pub struct Presets {
pub packages: HashSet<String>,
pub scripts: Vec<String>,
}
impl Presets {
pub fn load(list: &[PathBuf]) -> Result<Self, Error> {
let mut packages = HashSet::new();
let mut scripts = Vec::new();
for preset in list {
let Preset {
script,
packages: preset_packages,
} = Preset::load(&preset)?;
if let Some(preset_packages) = preset_packages {
packages.extend(preset_packages);
}
scripts.extend(script);
}
Ok(Self { packages, scripts })
}
}

View File

@@ -20,32 +20,32 @@ pub trait CommandExt {
impl CommandExt for Command {
fn run(&mut self, context: ErrorKind) -> Result<(), Error> {
let exit_status = self.spawn().context(context)?.wait().context(context)?;
let exit_status = self
.spawn()
.with_context(|_| context.clone())?
.wait()
.with_context(|_| context.clone())?;
if !exit_status.success() {
return Err(ProcessError::BadExitCode(exit_status)
.context(context)
.into());
Err(ProcessError::BadExitCode(exit_status)).with_context(|_| context.clone())?;
}
Ok(())
}
fn run_text_output(&mut self, context: ErrorKind) -> Result<String, Error> {
let output = self.output().context(context)?;
let output = self.output().with_context(|_| context.clone())?;
if !output.status.success() {
let error = str::from_utf8(&output.stderr).unwrap_or("[INVALID UTF8]");
error!("{}", error);
return Err(ProcessError::BadExitCode(output.status)
.context(context)
.into());
Err(ProcessError::BadExitCode(output.status)).with_context(|_| context.clone())?;
}
Ok(String::from(
str::from_utf8(&output.stdout)
.map_err(|_| ProcessError::InvalidUtf8)
.context(context)?,
.with_context(|_| context.clone())?,
))
}
}