Add shared_directories preset parameter (#35)

This commit is contained in:
James McMurray 2020-03-26 19:57:46 +01:00 committed by GitHub
parent 14347710ce
commit bc9969a0db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 100 additions and 12 deletions

View File

@ -97,6 +97,7 @@ Preset files are simple TOML files which contain:
* A list of packages to install: `packages = ["mypackage"]`
* A post-installation script: `script = """ ... """`
* Environment variables required by the preset (e.g. used in the script): `enironment_variables = ["USERNAME"]`
* A list of shared directories `shared_directories = ["subdirectory"]` - where subdirectory would be available at `/shared_dirs/subdirectory/` for use in the script of the preset.
See the presets directory for examples.
@ -106,6 +107,8 @@ Presets are used via the `--presets` argument (multiple preset files or director
sudo ALMA_USER=archie alma create /dev/disk/by-id/usb-Generic_USB_Flash_Disk-0:0 --presets ./presets/user.toml ./presets/custom_preset.toml
```
Preset scripts are executed in the same order they are provided.
If a directory is provided, then all files and subdirectories in the directory are recursively crawled in alphanumeric order (all files must be ALMA .toml files). This allows you to use the following structure to compose many scripts in a specific order:
```
@ -132,7 +135,7 @@ usermod -G wheel -a ${ALMA_USER}
environment_variables = ["ALMA_USER"]
```
Preset scripts are executed in the same order they are provided.
Note that shared directories in the preset scripts are mounted as bind mounts, so they are *not* mounted read-only. Any changes the custom script makes to the shared directory will be carried out in the preset shared directory of the host system, so be sure to copy (not move) files from the shared directories.
## Similar projects

5
presets/copy_file.toml Normal file
View File

@ -0,0 +1,5 @@
script = """
ls /shared_dirs/copy_file_example/
cp /shared_dirs/copy_file_example/testfile.txt /root/
"""
shared_directories = ["copy_file_example"]

View File

@ -0,0 +1 @@
test

View File

@ -25,7 +25,7 @@ use std::process::{exit, Command as ProcessCommand};
use std::thread;
use std::time::Duration;
use storage::EncryptedDevice;
use storage::{BlockDevice, Filesystem, FilesystemType, LoopDevice};
use storage::{BlockDevice, Filesystem, FilesystemType, LoopDevice, MountStack};
use structopt::StructOpt;
use tempfile::tempdir;
use tool::Tool;
@ -264,10 +264,33 @@ fn create(command: args::CreateCommand) -> Result<(), Error> {
}
for script in presets.scripts {
let mut bind_mount_stack = MountStack::new();
if let Some(shared_dirs) = &script.shared_dirs {
for dir in shared_dirs {
// Create shared directories mount points inside chroot
std::fs::create_dir_all(
mount_point
.path()
.join(PathBuf::from("shared_dirs/"))
.join(dir.file_name().unwrap()),
)
.context(ErrorKind::PresetScript)?;
// Bind mount shared directories
let target = mount_point
.path()
.join(PathBuf::from("shared_dirs/"))
.join(dir.file_name().unwrap());
bind_mount_stack
.bind_mount(dir.clone(), target, None)
.context(ErrorKind::Mounting)?;
}
}
let mut script_file =
tempfile::NamedTempFile::new_in(mount_point.path()).context(ErrorKind::PresetScript)?;
script_file
.write_all(script.as_bytes())
.write_all(script.script_text.as_bytes())
.and_then(|_| script_file.as_file_mut().metadata())
.and_then(|metadata| {
let mut permissions = metadata.permissions();

View File

@ -12,6 +12,7 @@ struct Preset {
packages: Option<Vec<String>>,
script: Option<String>,
environment_variables: Option<Vec<String>>,
shared_directories: Option<Vec<PathBuf>>,
}
fn visit_dirs(dir: &Path, filevec: &mut Vec<PathBuf>) -> Result<(), io::Error> {
@ -22,7 +23,9 @@ fn visit_dirs(dir: &Path, filevec: &mut Vec<PathBuf>) -> Result<(), io::Error> {
if path.is_dir() {
visit_dirs(&path, filevec)?;
} else {
filevec.push(entry.path());
if entry.path().extension() == Some(&std::ffi::OsString::from("toml")) {
filevec.push(entry.path());
}
}
}
}
@ -40,9 +43,10 @@ impl Preset {
fn process(
&self,
packages: &mut HashSet<String>,
scripts: &mut Vec<String>,
scripts: &mut Vec<Script>,
environment_variables: &mut HashSet<String>,
) {
path: &PathBuf,
) -> Result<(), ErrorKind> {
if let Some(preset_packages) = &self.packages {
packages.extend(preset_packages.clone());
}
@ -51,19 +55,52 @@ impl Preset {
environment_variables.extend(preset_environment_variables.clone());
}
scripts.extend(self.script.clone());
if let Some(script_text) = &self.script {
scripts.push(Script {
script_text: script_text.clone(),
shared_dirs: self
.shared_directories
.clone()
.map(|x| {
// Convert directories to absolute paths
// If any shared directory is not a directory then throw an error
x.iter()
.cloned()
.map(|y| {
let full_path = path.parent().unwrap().join(&y);
if full_path.is_dir() {
Ok(full_path)
} else {
Err(ErrorKind::Preset(format!(
"Preset: {} - shared directory: {} is not directory",
path.display(),
y.display()
)))
}
})
.collect::<Result<Vec<_>, ErrorKind>>()
})
.map_or(Ok(None), |r| r.map(Some))?,
});
}
Ok(())
}
}
pub struct Script {
pub script_text: String,
pub shared_dirs: Option<Vec<PathBuf>>,
}
pub struct PresetsCollection {
pub packages: HashSet<String>,
pub scripts: Vec<String>,
pub scripts: Vec<Script>,
}
impl PresetsCollection {
pub fn load(list: &[PathBuf]) -> Result<Self, Error> {
let mut packages = HashSet::new();
let mut scripts = Vec::new();
let mut scripts: Vec<Script> = Vec::new();
let mut environment_variables = HashSet::new();
for preset in list {
@ -83,17 +120,18 @@ impl PresetsCollection {
&mut packages,
&mut scripts,
&mut environment_variables,
);
&path,
)?;
}
} else {
Preset::load(&preset)?.process(
&mut packages,
&mut scripts,
&mut environment_variables,
);
&preset,
)?;
}
}
let missing_envrionments: Vec<String> = environment_variables
.into_iter()
.filter(|var| env::var(var).is_err())

View File

@ -38,6 +38,24 @@ impl<'a> MountStack<'a> {
Ok(())
}
pub fn bind_mount(
&mut self,
source: PathBuf,
target: PathBuf,
options: Option<&str>,
) -> nix::Result<()> {
debug!("Mounting {:?} to {:?}", source, target);
mount::<_, _, str, _>(
Some(&source),
&target,
None,
MsFlags::MS_BIND | MsFlags::MS_NOATIME, // Read-only flag has no effect for bind mounts
options,
)?;
self.targets.push(target);
Ok(())
}
fn _umount(&mut self) -> Result<(), Error> {
let mut result = Ok(());