mirror of
https://github.com/philmmanjaro/alma.git
synced 2025-07-26 06:59: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 list of packages to install: `packages = ["mypackage"]`
|
||||||
* A post-installation script: `script = """ ... """`
|
* A post-installation script: `script = """ ... """`
|
||||||
* Environment variables required by the preset (e.g. used in the script): `enironment_variables = ["USERNAME"]`
|
* 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.
|
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
|
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:
|
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"]
|
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
|
## 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::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use storage::EncryptedDevice;
|
use storage::EncryptedDevice;
|
||||||
use storage::{BlockDevice, Filesystem, FilesystemType, LoopDevice};
|
use storage::{BlockDevice, Filesystem, FilesystemType, LoopDevice, MountStack};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
use tool::Tool;
|
use tool::Tool;
|
||||||
@ -264,10 +264,33 @@ fn create(command: args::CreateCommand) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for script in presets.scripts {
|
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 =
|
let mut script_file =
|
||||||
tempfile::NamedTempFile::new_in(mount_point.path()).context(ErrorKind::PresetScript)?;
|
tempfile::NamedTempFile::new_in(mount_point.path()).context(ErrorKind::PresetScript)?;
|
||||||
script_file
|
script_file
|
||||||
.write_all(script.as_bytes())
|
.write_all(script.script_text.as_bytes())
|
||||||
.and_then(|_| script_file.as_file_mut().metadata())
|
.and_then(|_| script_file.as_file_mut().metadata())
|
||||||
.and_then(|metadata| {
|
.and_then(|metadata| {
|
||||||
let mut permissions = metadata.permissions();
|
let mut permissions = metadata.permissions();
|
||||||
|
@ -12,6 +12,7 @@ struct Preset {
|
|||||||
packages: Option<Vec<String>>,
|
packages: Option<Vec<String>>,
|
||||||
script: Option<String>,
|
script: Option<String>,
|
||||||
environment_variables: Option<Vec<String>>,
|
environment_variables: Option<Vec<String>>,
|
||||||
|
shared_directories: Option<Vec<PathBuf>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_dirs(dir: &Path, filevec: &mut Vec<PathBuf>) -> Result<(), io::Error> {
|
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() {
|
if path.is_dir() {
|
||||||
visit_dirs(&path, filevec)?;
|
visit_dirs(&path, filevec)?;
|
||||||
} else {
|
} 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(
|
fn process(
|
||||||
&self,
|
&self,
|
||||||
packages: &mut HashSet<String>,
|
packages: &mut HashSet<String>,
|
||||||
scripts: &mut Vec<String>,
|
scripts: &mut Vec<Script>,
|
||||||
environment_variables: &mut HashSet<String>,
|
environment_variables: &mut HashSet<String>,
|
||||||
) {
|
path: &PathBuf,
|
||||||
|
) -> Result<(), ErrorKind> {
|
||||||
if let Some(preset_packages) = &self.packages {
|
if let Some(preset_packages) = &self.packages {
|
||||||
packages.extend(preset_packages.clone());
|
packages.extend(preset_packages.clone());
|
||||||
}
|
}
|
||||||
@ -51,19 +55,52 @@ impl Preset {
|
|||||||
environment_variables.extend(preset_environment_variables.clone());
|
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 struct PresetsCollection {
|
||||||
pub packages: HashSet<String>,
|
pub packages: HashSet<String>,
|
||||||
pub scripts: Vec<String>,
|
pub scripts: Vec<Script>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PresetsCollection {
|
impl PresetsCollection {
|
||||||
pub fn load(list: &[PathBuf]) -> Result<Self, Error> {
|
pub fn load(list: &[PathBuf]) -> Result<Self, Error> {
|
||||||
let mut packages = HashSet::new();
|
let mut packages = HashSet::new();
|
||||||
let mut scripts = Vec::new();
|
let mut scripts: Vec<Script> = Vec::new();
|
||||||
let mut environment_variables = HashSet::new();
|
let mut environment_variables = HashSet::new();
|
||||||
|
|
||||||
for preset in list {
|
for preset in list {
|
||||||
@ -83,17 +120,18 @@ impl PresetsCollection {
|
|||||||
&mut packages,
|
&mut packages,
|
||||||
&mut scripts,
|
&mut scripts,
|
||||||
&mut environment_variables,
|
&mut environment_variables,
|
||||||
);
|
&path,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Preset::load(&preset)?.process(
|
Preset::load(&preset)?.process(
|
||||||
&mut packages,
|
&mut packages,
|
||||||
&mut scripts,
|
&mut scripts,
|
||||||
&mut environment_variables,
|
&mut environment_variables,
|
||||||
);
|
&preset,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let missing_envrionments: Vec<String> = environment_variables
|
let missing_envrionments: Vec<String> = environment_variables
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|var| env::var(var).is_err())
|
.filter(|var| env::var(var).is_err())
|
||||||
|
@ -38,6 +38,24 @@ impl<'a> MountStack<'a> {
|
|||||||
Ok(())
|
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> {
|
fn _umount(&mut self) -> Result<(), Error> {
|
||||||
let mut result = Ok(());
|
let mut result = Ok(());
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user