Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rust/treefile: Add basearch key #1766

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 42 additions & 23 deletions rust/src/treefile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ enum InputFormat {
JSON,
}

/// Parse a YAML treefile definition using architecture `arch`.
/// Parse a YAML treefile definition using base architecture `basearch`.
/// This does not open the externals.
fn treefile_parse_stream<R: io::Read>(
fmt: InputFormat,
input: &mut R,
arch: Option<&str>,
basearch: Option<&str>,
) -> Fallible<TreeComposeConfig> {
let mut treefile: TreeComposeConfig = match fmt {
InputFormat::YAML => {
Expand All @@ -96,11 +96,28 @@ fn treefile_parse_stream<R: io::Read>(
}
};

treefile.basearch = match (treefile.basearch, basearch) {
(Some(treearch), Some(arch)) => {
if treearch != arch {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Invalid basearch {} on {}: cross-composes are not supported",
treearch, arch),
).into())
} else {
Some(treearch)
}
}
(None, Some(arch)) => Some(arch.into()),
// really, only for tests do we not specify a basearch. let's just canonicalize to None
(_, None) => None,
};

// Substitute ${basearch}
treefile.treeref = match (arch, treefile.treeref.take()) {
(Some(arch), Some(treeref)) => {
treefile.treeref = match (basearch, treefile.treeref.take()) {
(Some(basearch), Some(treeref)) => {
let mut varsubsts = HashMap::new();
varsubsts.insert("basearch".to_string(), arch.to_string());
varsubsts.insert("basearch".to_string(), basearch.to_string());
Some(
utils::varsubst(&treeref, &varsubsts)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e.to_string()))?,
Expand All @@ -111,7 +128,7 @@ fn treefile_parse_stream<R: io::Read>(

// Special handling for packages, since we allow whitespace within items.
// We also canonicalize bootstrap_packages to packages here so it's
// easier to append the arch packages after.
// easier to append the basearch packages after.
let mut pkgs: Vec<String> = vec![];
{
if let Some(base_pkgs) = treefile.packages.take() {
Expand All @@ -122,7 +139,7 @@ fn treefile_parse_stream<R: io::Read>(
}
}

let arch_pkgs = match arch {
let arch_pkgs = match basearch {
Some("aarch64") => treefile.packages_aarch64.take(),
Some("armhfp") => treefile.packages_armhfp.take(),
Some("ppc64") => treefile.packages_ppc64.take(),
Expand Down Expand Up @@ -164,7 +181,7 @@ fn load_passwd_file<P: AsRef<Path>>(
/// open its external files.
fn treefile_parse<P: AsRef<Path>>(
filename: P,
arch: Option<&str>,
basearch: Option<&str>,
) -> Fallible<ConfigAndExternals> {
let filename = filename.as_ref();
let mut f = io::BufReader::new(open_file(filename)?);
Expand All @@ -177,7 +194,7 @@ fn treefile_parse<P: AsRef<Path>>(
} else {
InputFormat::JSON
};
let tf = treefile_parse_stream(fmt, &mut f, arch).map_err(|e| {
let tf = treefile_parse_stream(fmt, &mut f, basearch).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!("Parsing {}: {}", filename.to_string_lossy(), e.to_string()),
Expand Down Expand Up @@ -302,11 +319,11 @@ fn treefile_merge_externals(dest: &mut TreefileExternals, src: &mut TreefileExte
/// Recursively parse a treefile, merging along the way.
fn treefile_parse_recurse<P: AsRef<Path>>(
filename: P,
arch: Option<&str>,
basearch: Option<&str>,
depth: u32,
) -> Fallible<ConfigAndExternals> {
let filename = filename.as_ref();
let mut parsed = treefile_parse(filename, arch)?;
let mut parsed = treefile_parse(filename, basearch)?;
let include_path = parsed.config.include.take();
if let &Some(ref include_path) = &include_path {
if depth == INCLUDE_MAXDEPTH {
Expand All @@ -317,7 +334,7 @@ fn treefile_parse_recurse<P: AsRef<Path>>(
}
let parent = filename.parent().unwrap();
let include_path = parent.join(include_path);
let mut included = treefile_parse_recurse(include_path, arch, depth + 1)?;
let mut included = treefile_parse_recurse(include_path, basearch, depth + 1)?;
treefile_merge(&mut parsed.config, &mut included.config);
treefile_merge_externals(&mut parsed.externals, &mut included.externals);
}
Expand All @@ -340,10 +357,10 @@ impl Treefile {
/// The main treefile creation entrypoint.
fn new_boxed(
filename: &Path,
arch: Option<&str>,
basearch: Option<&str>,
workdir: openat::Dir,
) -> Fallible<Box<Treefile>> {
let parsed = treefile_parse_recurse(filename, arch, 0)?;
let parsed = treefile_parse_recurse(filename, basearch, 0)?;
Treefile::validate_config(&parsed.config)?;
let dfd = openat::Dir::open(filename.parent().unwrap())?;
let (rojig_name, rojig_spec) = if let Some(rojig) = parsed.config.rojig.as_ref() {
Expand Down Expand Up @@ -501,6 +518,8 @@ struct TreeComposeConfig {
#[serde(rename = "ref")]
#[serde(skip_serializing_if = "Option::is_none")]
treeref: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
basearch: Option<String>,
// Optional rojig data
#[serde(skip_serializing_if = "Option::is_none")]
rojig: Option<Rojig>,
Expand Down Expand Up @@ -752,15 +771,15 @@ remove-files:
}

impl TreefileTest {
fn new<'a, 'b>(contents: &'a str, arch: Option<&'b str>) -> Fallible<TreefileTest> {
fn new<'a, 'b>(contents: &'a str, basearch: Option<&'b str>) -> Fallible<TreefileTest> {
let workdir = tempfile::tempdir()?;
let tf_path = workdir.path().join("treefile.yaml");
{
let mut tf_stream = io::BufWriter::new(fs::File::create(&tf_path)?);
tf_stream.write_all(contents.as_bytes())?;
}
let tf =
Treefile::new_boxed(tf_path.as_path(), arch, openat::Dir::open(workdir.path())?)?;
Treefile::new_boxed(tf_path.as_path(), basearch, openat::Dir::open(workdir.path())?)?;
Ok(TreefileTest { tf, workdir })
}
}
Expand Down Expand Up @@ -796,18 +815,18 @@ rojig:

#[test]
fn test_treefile_merge() {
let arch = Some(ARCH_X86_64);
let basearch = Some(ARCH_X86_64);
let mut base_input = io::BufReader::new(VALID_PRELUDE.as_bytes());
let mut base = treefile_parse_stream(InputFormat::YAML, &mut base_input, arch).unwrap();
let mut base = treefile_parse_stream(InputFormat::YAML, &mut base_input, basearch).unwrap();
let mut mid_input = io::BufReader::new(
r###"
packages:
- some layered packages
"###.as_bytes(),
);
let mut mid = treefile_parse_stream(InputFormat::YAML, &mut mid_input, arch).unwrap();
let mut mid = treefile_parse_stream(InputFormat::YAML, &mut mid_input, basearch).unwrap();
let mut top_input = io::BufReader::new(ROJIG_YAML.as_bytes());
let mut top = treefile_parse_stream(InputFormat::YAML, &mut top_input, arch).unwrap();
let mut top = treefile_parse_stream(InputFormat::YAML, &mut top_input, basearch).unwrap();
treefile_merge(&mut mid, &mut base);
treefile_merge(&mut top, &mut mid);
let tf = &top;
Expand Down Expand Up @@ -849,18 +868,18 @@ mod ffi {
#[no_mangle]
pub extern "C" fn ror_treefile_new(
filename: *const libc::c_char,
arch: *const libc::c_char,
basearch: *const libc::c_char,
workdir_dfd: libc::c_int,
gerror: *mut *mut glib_sys::GError,
) -> *mut Treefile {
// Convert arguments
let filename = ffi_view_os_str(filename);
let arch = ffi_view_nullable_str(arch);
let basearch = ffi_view_nullable_str(basearch);
let workdir = ffi_view_openat_dir(workdir_dfd);
// Run code, map error if any, otherwise extract raw pointer, passing
// ownership back to C.
ptr_glib_error(
Treefile::new_boxed(filename.as_ref(), arch, workdir),
Treefile::new_boxed(filename.as_ref(), basearch, workdir),
gerror,
)
}
Expand Down
13 changes: 13 additions & 0 deletions tests/common/libtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -562,3 +562,16 @@ assert_files_hardlinked() {
fatal "Files '$1' and '$2' are not hardlinked"
fi
}

# $1 - json file
# $2+ - assertions
assert_jq() {
f=$1; shift
for expression in "$@"; do
if ! jq -e "${expression}" >/dev/null < $f; then
jq . < $f | sed -e 's/^/# /' >&2
echo 1>&2 "${expression} failed to match $f"
exit 1
fi
done
}
10 changes: 1 addition & 9 deletions tests/common/libvm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -354,15 +354,7 @@ vm_assert_layered_pkg() {
# and asserts that they are true.
vm_assert_status_jq() {
vm_rpmostree status --json > status.json
vm_rpmostree status > status.txt
for expression in "$@"; do
if ! jq -e "${expression}" >/dev/null < status.json; then
jq . < status.json | sed -e 's/^/# /' >&2
echo 1>&2 "${expression} failed to match status.json"
cat status.txt
exit 1
fi
done
assert_jq status.json "$@"
}

vm_pending_is_staged() {
Expand Down
4 changes: 4 additions & 0 deletions tests/compose-tests/libbasic-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,7 @@ assert_file_has_content pkglist.txt 'systemd'
assert_file_has_content pkglist.txt 'systemd-bootchart'
echo "ok compose pkglist"
}

ostree --repo=${repobuild} cat ${treeref} /usr/share/rpm-ostree/treefile.json > treefile.json
assert_jq treefile.json '.basearch == "x86_64"'
echo "ok basearch"