+2
-3
@@ -76,9 +76,8 @@ fn build_inner(
|
||||
) -> Result<()> {
|
||||
let config = inject::inspect_config(image)?;
|
||||
let command = cmd.unwrap_or_else(|| inject::infer_command(&config));
|
||||
let env = inject::env(&config);
|
||||
let working_dir = inject::working_dir(&config);
|
||||
let exec_script = inject::build_exec_script(&command, &env, working_dir);
|
||||
let exec_script =
|
||||
inject::build_exec_script(&command, &config.env, config.working_dir.as_deref());
|
||||
inject::inject(mount_path, &exec_script)?;
|
||||
println!("Injected /slim/ (init + exec)");
|
||||
|
||||
|
||||
+18
-35
@@ -3,6 +3,7 @@
|
||||
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use serde::Deserialize;
|
||||
use std::fmt::Write as _;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -14,13 +15,13 @@ const SLIM_INIT: &str = include_str!("scripts/slim-init.sh");
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct Config {
|
||||
#[serde(default)]
|
||||
cmd: Option<Vec<String>>,
|
||||
pub cmd: Vec<String>,
|
||||
#[serde(default)]
|
||||
entrypoint: Option<Vec<String>>,
|
||||
pub entrypoint: Vec<String>,
|
||||
#[serde(default)]
|
||||
env: Option<Vec<String>>,
|
||||
pub env: Vec<String>,
|
||||
#[serde(default)]
|
||||
working_dir: Option<String>,
|
||||
pub working_dir: Option<String>,
|
||||
}
|
||||
|
||||
pub fn inspect_config(image: &str) -> Result<Config> {
|
||||
@@ -42,40 +43,21 @@ pub fn inspect_config(image: &str) -> Result<Config> {
|
||||
/// Concatenates ENTRYPOINT + CMD (Docker semantics). If neither is present,
|
||||
/// falls back to `/bin/sh`.
|
||||
pub fn infer_command(config: &Config) -> String {
|
||||
let entrypoint = config.entrypoint.as_deref().filter(|e| !e.is_empty());
|
||||
let cmd = config.cmd.as_deref().filter(|c| !c.is_empty());
|
||||
let parts = config.entrypoint.iter().chain(config.cmd.iter());
|
||||
let parts: Vec<_> = parts.map(|s| s.as_str()).collect();
|
||||
|
||||
let parts: Vec<String> = match (entrypoint, cmd) {
|
||||
(Some(ep), Some(c)) => {
|
||||
let mut parts = ep.to_vec();
|
||||
parts.extend(c.iter().cloned());
|
||||
parts
|
||||
match &parts[..] {
|
||||
[] => "/bin/sh".into(),
|
||||
["/bin/sh" | "sh", "-c", cmd] => cmd.to_string(),
|
||||
_ => shell_join(&parts),
|
||||
}
|
||||
(Some(ep), None) => ep.to_vec(),
|
||||
(None, Some(c)) => c.to_vec(),
|
||||
(None, None) => vec!["/bin/sh".to_string()],
|
||||
};
|
||||
|
||||
if parts.len() == 3 && (parts[0] == "/bin/sh" || parts[0] == "sh") && parts[1] == "-c" {
|
||||
parts[2].clone()
|
||||
} else {
|
||||
shell_join(&parts)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn env(config: &Config) -> Vec<String> {
|
||||
config.env.clone().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn working_dir(config: &Config) -> Option<&str> {
|
||||
config.working_dir.as_deref()
|
||||
}
|
||||
|
||||
fn shell_escape(s: &str) -> String {
|
||||
format!("'{}'", s.replace('\'', "'\\''"))
|
||||
}
|
||||
|
||||
fn shell_join(parts: &[String]) -> String {
|
||||
fn shell_join(parts: &[&str]) -> String {
|
||||
parts
|
||||
.iter()
|
||||
.map(|p| shell_escape(p))
|
||||
@@ -87,18 +69,19 @@ fn shell_join(parts: &[String]) -> String {
|
||||
pub fn build_exec_script(command: &str, env: &[String], working_dir: Option<&str>) -> String {
|
||||
let mut lines = String::from("#!/bin/sh\n");
|
||||
if let Some(dir) = working_dir.filter(|d| !d.is_empty()) {
|
||||
lines.push_str(&format!("cd {} 2>/dev/null\n", shell_escape(dir)));
|
||||
_ = writeln!(&mut lines, "cd {} 2>/dev/null", shell_escape(dir));
|
||||
}
|
||||
for var in env {
|
||||
if let Some((key, val)) = var.split_once('=') {
|
||||
lines.push_str(&format!(
|
||||
"export {}={}\n",
|
||||
_ = writeln!(
|
||||
&mut lines,
|
||||
"export {}={}",
|
||||
shell_escape(key),
|
||||
shell_escape(val)
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
lines.push_str(&format!("exec /bin/sh -c {}\n", shell_escape(command)));
|
||||
_ = writeln!(&mut lines, "exec /bin/sh -c {}", shell_escape(command));
|
||||
lines
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user