treefile: allow ${releasever} in more keys

Besides allowing ${releasever}, only do the substitution as the final
pass after merging the treefiles for all the keys (currently ${basearch}
and ${releasever}) instead of doing it per parse. This way we have the
expected semantics where one could do:

```
include: "fedora-coreos.yaml"
releasever: "42"
```

and have that releasever used.

Fixes #1809

Signed-off-by: Rafael Fonseca <r4f4rfs@gmail.com>

Closes: #1848
Approved by: cgwalters
This commit is contained in:
Rafael Fonseca 2019-06-04 11:09:35 +02:00 committed by Atomic Bot
parent d0f90ca4dd
commit c94bd08b02
3 changed files with 80 additions and 18 deletions

10
rust/Cargo.lock generated
View File

@ -216,6 +216,14 @@ name = "encode_unicode"
version = "0.3.5" version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "envsubst"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "failure" name = "failure"
version = "0.1.5" version = "0.1.5"
@ -631,6 +639,7 @@ dependencies = [
"c_utf8 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "c_utf8 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"curl 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", "curl 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
"envsubst 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -937,6 +946,7 @@ dependencies = [
"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd"
"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
"checksum encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90b2c9496c001e8cb61827acdefad780795c42264c137744cae6f7d9e3450abd" "checksum encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90b2c9496c001e8cb61827acdefad780795c42264c137744cae6f7d9e3450abd"
"checksum envsubst 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13cf19a8d534c83456ea13365a1935810a39f0e43bf1ec9371077e1966da396a"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"

View File

@ -23,6 +23,7 @@ c_utf8 = "0.1.0"
systemd = "0.4.0" systemd = "0.4.0"
indicatif = "0.11.0" indicatif = "0.11.0"
lazy_static = "1.1.0" lazy_static = "1.1.0"
envsubst = "0.1.0"
[lib] [lib]
name = "rpmostree_rust" name = "rpmostree_rust"

View File

@ -130,19 +130,6 @@ fn treefile_parse_stream<R: io::Read>(
.into()); .into());
} }
// Substitute ${basearch}
treefile.treeref = match (basearch, treefile.treeref.take()) {
(Some(basearch), Some(treeref)) => {
let mut varsubsts = HashMap::new();
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()))?,
)
}
(_, v) => v,
};
// Special handling for packages, since we allow whitespace within items. // Special handling for packages, since we allow whitespace within items.
// We also canonicalize bootstrap_packages to packages here so it's // We also canonicalize bootstrap_packages to packages here so it's
// easier to append the basearch packages after. // easier to append the basearch packages after.
@ -410,7 +397,8 @@ impl Treefile {
basearch: Option<&str>, basearch: Option<&str>,
workdir: openat::Dir, workdir: openat::Dir,
) -> Fallible<Box<Treefile>> { ) -> Fallible<Box<Treefile>> {
let parsed = treefile_parse_recurse(filename, basearch, 0)?; let mut parsed = treefile_parse_recurse(filename, basearch, 0)?;
parsed.config = parsed.config.substitute_vars()?;
Treefile::validate_config(&parsed.config)?; Treefile::validate_config(&parsed.config)?;
let dfd = openat::Dir::open(filename.parent().unwrap())?; let dfd = openat::Dir::open(filename.parent().unwrap())?;
let (rojig_name, rojig_spec) = if let Some(rojig) = parsed.config.rojig.as_ref() { let (rojig_name, rojig_spec) = if let Some(rojig) = parsed.config.rojig.as_ref() {
@ -718,6 +706,39 @@ impl TreeComposeConfig {
Ok(self) Ok(self)
} }
/// Look for use of ${variable} and replace it by its proper value
fn substitute_vars(mut self) -> Fallible<Self> {
let mut substvars: collections::HashMap<String, String> = collections::HashMap::new();
// Substitute ${basearch} and ${releasever}
if let Some(arch) = &self.basearch {
substvars.insert("basearch".to_string(), arch.clone());
}
if let Some(releasever) = &self.releasever {
substvars.insert("releasever".to_string(), releasever.clone());
}
envsubst::validate_vars(&substvars)?;
macro_rules! substitute_field {
( $field:ident ) => {{
if let Some(value) = self.$field.take() {
self.$field = if envsubst::is_templated(&value) {
match envsubst::substitute(value, &substvars) {
Ok(s) => Some(s),
Err(e) => return Err(e),
}
} else {
Some(value)
}
}
}};
};
substitute_field!(treeref);
substitute_field!(automatic_version_prefix);
substitute_field!(mutate_os_release);
Ok(self)
}
} }
#[cfg(test)] #[cfg(test)]
@ -753,8 +774,9 @@ packages-s390x:
#[test] #[test]
fn basic_valid() { fn basic_valid() {
let mut input = io::BufReader::new(VALID_PRELUDE.as_bytes()); let mut input = io::BufReader::new(VALID_PRELUDE.as_bytes());
let treefile = let mut treefile =
treefile_parse_stream(InputFormat::YAML, &mut input, Some(ARCH_X86_64)).unwrap(); treefile_parse_stream(InputFormat::YAML, &mut input, Some(ARCH_X86_64)).unwrap();
treefile = treefile.substitute_vars().unwrap();
assert!(treefile.treeref.unwrap() == "exampleos/x86_64/blah"); assert!(treefile.treeref.unwrap() == "exampleos/x86_64/blah");
assert!(treefile.packages.unwrap().len() == 5); assert!(treefile.packages.unwrap().len() == 5);
} }
@ -785,8 +807,9 @@ remove-files:
#[test] #[test]
fn basic_js_valid() { fn basic_js_valid() {
let mut input = io::BufReader::new(VALID_PRELUDE_JS.as_bytes()); let mut input = io::BufReader::new(VALID_PRELUDE_JS.as_bytes());
let treefile = let mut treefile =
treefile_parse_stream(InputFormat::JSON, &mut input, Some(ARCH_X86_64)).unwrap(); treefile_parse_stream(InputFormat::JSON, &mut input, Some(ARCH_X86_64)).unwrap();
treefile = treefile.substitute_vars().unwrap();
assert!(treefile.treeref.unwrap() == "exampleos/x86_64/blah"); assert!(treefile.treeref.unwrap() == "exampleos/x86_64/blah");
assert!(treefile.packages.unwrap().len() == 5); assert!(treefile.packages.unwrap().len() == 5);
} }
@ -794,7 +817,8 @@ remove-files:
#[test] #[test]
fn basic_valid_noarch() { fn basic_valid_noarch() {
let mut input = io::BufReader::new(VALID_PRELUDE.as_bytes()); let mut input = io::BufReader::new(VALID_PRELUDE.as_bytes());
let treefile = treefile_parse_stream(InputFormat::YAML, &mut input, None).unwrap(); let mut treefile = treefile_parse_stream(InputFormat::YAML, &mut input, None).unwrap();
treefile = treefile.substitute_vars().unwrap();
assert!(treefile.treeref.unwrap() == "exampleos/x86_64/blah"); assert!(treefile.treeref.unwrap() == "exampleos/x86_64/blah");
assert!(treefile.packages.unwrap().len() == 3); assert!(treefile.packages.unwrap().len() == 3);
} }
@ -802,7 +826,9 @@ remove-files:
fn append_and_parse(append: &'static str) -> TreeComposeConfig { fn append_and_parse(append: &'static str) -> TreeComposeConfig {
let buf = VALID_PRELUDE.to_string() + append; let buf = VALID_PRELUDE.to_string() + append;
let mut input = io::BufReader::new(buf.as_bytes()); let mut input = io::BufReader::new(buf.as_bytes());
treefile_parse_stream(InputFormat::YAML, &mut input, Some(ARCH_X86_64)).unwrap() let treefile =
treefile_parse_stream(InputFormat::YAML, &mut input, Some(ARCH_X86_64)).unwrap();
treefile.substitute_vars().unwrap()
} }
fn test_invalid(data: &'static str) { fn test_invalid(data: &'static str) {
@ -817,6 +843,31 @@ remove-files:
} }
} }
#[test]
fn basic_valid_releasever() {
let buf = r###"
ref: "exampleos/${basearch}/${releasever}"
releasever: 30
automatic-version-prefix: ${releasever}
mutate-os-release: ${releasever}
"###;
let mut input = io::BufReader::new(buf.as_bytes());
let mut treefile =
treefile_parse_stream(InputFormat::YAML, &mut input, Some(ARCH_X86_64)).unwrap();
treefile = treefile.substitute_vars().unwrap();
assert!(treefile.treeref.unwrap() == "exampleos/x86_64/30");
assert!(treefile.releasever.unwrap() == "30");
assert!(treefile.automatic_version_prefix.unwrap() == "30");
assert!(treefile.mutate_os_release.unwrap() == "30");
}
#[test]
fn test_valid_no_releasever() {
let treefile = append_and_parse("automatic_version_prefix: ${releasever}");
assert!(treefile.releasever == None);
assert!(treefile.automatic_version_prefix.unwrap() == "${releasever}");
}
#[test] #[test]
fn basic_valid_legacy() { fn basic_valid_legacy() {
let treefile = append_and_parse( let treefile = append_and_parse(