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