mirror of
https://github.com/philmmanjaro/alma.git
synced 2025-07-24 22:19:28 +02:00
Add shared_directories preset parameter (#35)
This commit is contained in:
parent
14347710ce
commit
bc9969a0db
@ -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
5
presets/copy_file.toml
Normal 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"]
|
1
presets/copy_file_example/testfile.txt
Normal file
1
presets/copy_file_example/testfile.txt
Normal file
@ -0,0 +1 @@
|
||||
test
|
27
src/main.rs
27
src/main.rs
@ -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();
|
||||
|
@ -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())
|
||||
|
@ -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(());
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user