Skip to content

Commit

Permalink
image: add basic support for overlay of a proto directory onto an image
Browse files Browse the repository at this point in the history
  • Loading branch information
jclulow committed Mar 6, 2023
1 parent 5292491 commit 47ae558
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 9 deletions.
3 changes: 3 additions & 0 deletions image/templates/gimlet/zfs-compliance.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@
"extsrc": "faux-mgs",
"owner": "root", "group": "bin", "mode": "0755" },

{ "t": "include", "with": "genproto",
"name": "genproto", "file": "${genproto}" },

{ "t": "seed_smf", "apply_site": true }
]
}
3 changes: 3 additions & 0 deletions image/templates/gimlet/zfs-recovery.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
"extsrc": "pilot/${pilot_profile}.toml",
"owner": "root", "group": "bin", "mode": "0644" },

{ "t": "include", "with": "genproto",
"name": "genproto", "file": "${genproto}" },

{ "t": "seed_smf", "apply_site": true }
]
}
3 changes: 3 additions & 0 deletions image/templates/gimlet/zfs.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
"src": "mfg.xml",
"owner": "root", "group": "sys", "mode": "0644" },

{ "t": "include", "with": "genproto",
"name": "genproto", "file": "${genproto}" },

{ "t": "include", "name": "smf-reduce" },
{ "t": "seed_smf", "apply_site": true, "skip_seed": true }
]
Expand Down
4 changes: 1 addition & 3 deletions tools/helios-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ edition = "2018"
#
rust-version = "1.65.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["vendored-openssl"]
vendored-openssl = ['openssl/vendored']
Expand Down Expand Up @@ -38,4 +36,4 @@ regex = "1.4"
#
openssl = { version = "0.10", optional = true }
json5 = "0.4.1"
time = { version = "0.3", feature = ["formatting"] }
time = { version = "0.3" }
26 changes: 25 additions & 1 deletion tools/helios-build/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::sync::Mutex;
use serde::Deserialize;
use std::fs::File;
use std::io::{Read, BufReader};
use std::path::Path;
use std::path::{Path, PathBuf};
use anyhow::{Result, bail};

pub use slog::{info, warn, error, debug, trace, o};
Expand Down Expand Up @@ -121,3 +121,27 @@ pub fn exists_dir<P: AsRef<Path>>(path: P) -> Result<bool> {
Ok(false)
}
}

pub fn unprefix(prefix: &Path, path: &Path) -> Result<PathBuf> {
if prefix.is_absolute() != path.is_absolute() {
bail!("prefix and path must not be a mix of absolute and relative");
}

let cprefix = prefix.components().collect::<Vec<_>>();
let cpath = path.components().collect::<Vec<_>>();

if let Some(tail) = cpath.strip_prefix(cprefix.as_slice()) {
Ok(tail.iter().collect())
} else {
bail!("{:?} does not start with prefix {:?}", path, prefix);
}
}

pub fn reprefix(prefix: &Path, path: &Path, target: &Path) -> Result<PathBuf> {
if !target.is_absolute() {
bail!("target must be absolute");
}
let mut newpath = target.to_path_buf();
newpath.push(unprefix(prefix, path)?);
Ok(newpath)
}
149 changes: 144 additions & 5 deletions tools/helios-build/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ use common::*;
use anyhow::{Result, Context, bail};
use serde::Deserialize;
use std::collections::HashMap;
use std::os::unix::fs::PermissionsExt;
use std::process::Command;
use std::os::unix::process::CommandExt;
use std::io::{BufReader, Read};
use std::io::{BufReader, Read, Write};
use std::fs::File;
use std::time::{Instant,SystemTime};
use slog::Logger;
Expand Down Expand Up @@ -1007,10 +1008,6 @@ fn cmd_build_omnios(ca: &CommandArg) -> Result<()> {
return Ok(());
}

// if res.free.is_empty() {
// bail!("which package should I build?");
// }

let dir = top_path(&["projects", "omnios-build", "build"])?;

let mut pkgs = extract_pkgs(log, &dir)?;
Expand All @@ -1036,6 +1033,106 @@ fn cargo_target_cmd(project: &str, command: &str, debug: bool)
Ok(bin.to_str().unwrap().to_string())
}

/*
* If we have been provided an extra proto directory, we want to include all of
* the files and directories and symbolic links that have been assembled in that
* proto area in the final image. The image-builder tool cannot do this
* natively because there is no way to know what metadata to use for the files
* without some kind of explicit manifest provided as input to ensure_*
* directives.
*
* For our purposes here, it seems sufficient to use the mode bits as-is and
* just request that root own the files in the resultant image. We generate a
* partial template by walking the proto area, for inclusion when the "genproto"
* feature is also enabled in our main template.
*/
fn genproto(proto: &Path, output_template: &Path) -> Result<()> {
let rootdir = PathBuf::from("/");
let mut steps: Vec<serde_json::Value> = Default::default();

for ent in WalkDir::new(proto).min_depth(1).into_iter() {
let ent = ent?;

let relpath = unprefix(proto, ent.path())?;
if relpath == PathBuf::from("bin") {
/*
* On illumos, /bin is always a symbolic link to /usr/bin.
*/
bail!("proto {:?} contains a /bin directory; should use /usr/bin",
proto);
}

/*
* Use the relative path within the proto area as the absolute path
* in the image; e.g., "proto/bin/id" would become "/bin/id" in the
* image.
*/
let path = reprefix(proto, ent.path(), &rootdir)?;
let path = path.to_str().unwrap();

let md = ent.metadata()?;
let mode = format!("{:o}", md.permissions().mode() & 0o777);
if md.file_type().is_symlink() {
let target = std::fs::read_link(ent.path())?;

steps.push(serde_json::json!({
"t": "ensure_symlink", "link": path, "target": target,
"owner": "root", "group": "root",
}));
} else if md.file_type().is_dir() {
/*
* Some system directories are owned by groups other than "root".
* The rules are not exact; this is an approximation to reduce
* churn:
*/
let group = if relpath.starts_with("var")
|| relpath.starts_with("etc")
|| relpath.starts_with("lib/svc/manifest")
|| relpath.starts_with("platform")
|| relpath.starts_with("kernel")
|| relpath == PathBuf::from("usr")
|| relpath == PathBuf::from("usr/share")
|| relpath.starts_with("usr/platform")
|| relpath.starts_with("usr/kernel")
|| relpath == PathBuf::from("opt")
{
"sys"
} else if relpath.starts_with("lib")
|| relpath.starts_with("usr")
{
"bin"
} else {
"root"
};

steps.push(serde_json::json!({
"t": "ensure_dir", "dir": path,
"owner": "root", "group": group, "mode": mode,
}));
} else if md.file_type().is_file() {
steps.push(serde_json::json!({
"t": "ensure_file", "file": path, "extsrc": relpath,
"owner": "root", "group": "root", "mode": mode,
}));
} else {
bail!("unhandled file type at {:?}", ent.path());
}
}

let out = serde_json::to_vec_pretty(&serde_json::json!({
"steps": steps,
}))?;
let mut f = std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(output_template)?;
f.write_all(&out)?;
f.flush()?;

Ok(())
}

fn cmd_image(ca: &CommandArg) -> Result<()> {
let mut opts = baseopts();
opts.optflag("d", "", "use DEBUG packages");
Expand All @@ -1050,6 +1147,7 @@ fn cmd_image(ca: &CommandArg) -> Result<()> {
opts.optmulti("X", "", "skip this phase", "PHASE");
opts.optflag("", "ddr-testing", "build ROMs for other DDR frequencies");
opts.optopt("p", "", "use an external package repository", "PUBLISHER=URL");
opts.optopt("P", "", "include all files from extra proto area", "DIR");

let usage = || {
println!("{}",
Expand All @@ -1073,6 +1171,16 @@ fn cmd_image(ca: &CommandArg) -> Result<()> {
let skips = res.opt_strs("X");
let recovery = res.opt_present("R");

let extra_proto = if let Some(dir) = res.opt_str("P") {
let dir = PathBuf::from(dir);
if !dir.is_dir() {
bail!("-P must specify a proto area directory");
}
Some(dir)
} else {
None
};

let user = illumos::get_username()?.unwrap_or("unknown".to_string());

let image_name = res.opt_str("N").unwrap_or_else(|| {
Expand Down Expand Up @@ -1159,6 +1267,25 @@ fn cmd_image(ca: &CommandArg) -> Result<()> {

let tempdir = ensure_dir(&["tmp", &timage])?;

let genproto = {
let p = rel_path(Some(&tempdir), &["genproto.json"])?;
if p.exists() {
/*
* Remove the old template file to ensure we do not accidentally use
* a stale copy later.
*/
std::fs::remove_file(&p)?;
}

if let Some(dir) = extra_proto.as_deref() {
genproto(dir, &p)?;
info!(log, "generated template {:?} for extra proto {:?}", p, dir);
Some(p)
} else {
None
}
};

let repo = if let Some(extrepo) = &extrepo {
/*
* If we have been instructed to use a repository URL, we do not need to
Expand Down Expand Up @@ -1193,6 +1320,11 @@ fn cmd_image(ca: &CommandArg) -> Result<()> {
cmd.arg("-d").arg(&imgds);
cmd.arg("-g").arg("gimlet");
cmd.arg("-T").arg(&templates);
if let Some(genproto) = &genproto {
cmd.arg("-E").arg(extra_proto.as_deref().unwrap());
cmd.arg("-F").arg(&format!("genproto={}",
genproto.to_str().unwrap()));
}
if let Some(cdock) = &cdock {
cmd.arg("-F").arg("compliance");
cmd.arg("-F").arg("stress");
Expand Down Expand Up @@ -1821,6 +1953,13 @@ fn main() -> Result<()> {
hide: false,
blank: false,
});
handlers.push(CommandInfo {
name: "image".into(),
desc: "experimental image construction for Gimlets".into(),
func: cmd_image,
hide: true,
blank: false,
});

let usage = || {
let mut out = String::new();
Expand Down

0 comments on commit 47ae558

Please sign in to comment.