use std::io::Write as _;
use flate2::Compression;
use flate2::write::GzEncoder;
use isoloom_plan::configure::Resource;
use isoloom_spec::shell::base64;
use super::{Configured, shell};
pub const DIR: &str = "/var/lib/isoloom/configure";
pub const LABEL: &str = "its configuration";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WriteFile {
pub path: String,
pub permissions: &'static str,
pub content: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Configure {
pub file: WriteFile,
pub command: String,
}
#[must_use]
pub fn configure(c: &Configured<'_>, resources: &[Resource], fresh: bool, index: usize) -> Configure {
let path = format!("{DIR}/configure-{index}.sh");
let command = format!("sh {path}");
let file = WriteFile {
path,
permissions: "0700",
content: gz_b64(&shell::script(c, resources, fresh)),
};
Configure { file, command }
}
#[must_use]
pub fn gz_b64(script: &str) -> String {
let mut gz = GzEncoder::new(Vec::new(), Compression::best());
let written = gz.write_all(script.as_bytes());
let bytes = written.and(gz.finish()).unwrap_or_default();
base64(&bytes)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Limit {
Aws,
Azure,
Gcp,
}
impl Limit {
#[must_use]
pub fn bytes(self) -> usize {
match self {
Limit::Aws => 16 * 1024,
Limit::Azure => 64 * 1024,
Limit::Gcp => 256 * 1024,
}
}
#[must_use]
pub fn provider(self) -> &'static str {
match self {
Limit::Aws => "AWS",
Limit::Azure => "Azure",
Limit::Gcp => "Google Cloud",
}
}
pub fn fits(self, user_data: &str) -> Result<(), TooBig> {
let size = user_data.len();
let cap = self.bytes();
if size <= cap {
Ok(())
} else {
Err(TooBig {
provider: self.provider(),
size,
cap,
})
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("its cloud-init user data is {size} bytes, over {provider}'s {cap}-byte limit: fewer or smaller files, or a smaller recipe")]
#[non_exhaustive]
pub struct TooBig {
pub provider: &'static str,
pub size: usize,
pub cap: usize,
}
#[cfg(test)]
mod tests;