-
Notifications
You must be signed in to change notification settings - Fork 195
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
🦀 Add support for YAML treefiles #1377
Conversation
One cool side-effect of the way this is implemented here is that we get implicit support for I did have to add a few |
rust/src/treefile.rs
Outdated
|
||
match treefile_read_impl(filename_path, output_file) { | ||
Err(e) => { | ||
// just write it out to stderr for now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A whole world I haven't looked at is the glib-rs bits. If we were to do this more seriously we'd probably need to pull that in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I spent some time trying to figure out how to use this, but it seems like these bindings are more meant for Rust to interact with glib rather than Rust pretending to be glib. E.g. there's an Error::wrap()
method for wrapping GErrors received from glib, but I don't know how to create an Error::new()
and pass it back as an unowned GError
to rpm-ostree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jlebon I think you are looking for https://docs.rs/glib-sys/0.6.0/glib_sys/struct.GError.html, which can be directly built by consumers (I am not familiar with glib, so I don't know who is supposed to own message
). That said I think you don't want to pull all of glib-rs
in here (as it will likely create linkage issues with its build.rs
), but only some ctypes definitions for exchange at FFI border.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lucab Thanks for the pointer. I finally got this working by just directly using the glib-sys
binding for g_error_new_literal
. This works around the main hurdle of somehow having to pass ownership of an allocated string on the Rust side to C to be freed by free
, which AFAICT is tricky business. OTOH, g_error_new_literal
just does an strdup
.
I made this into a trait to make it extra easy to use! :)
rust/src/treefile.rs
Outdated
match treefile_read_impl(filename_path, output_file) { | ||
Err(e) => { | ||
// just write it out to stderr for now | ||
let _ = writeln!(io::stderr(), "treefile error: {}", e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eprintln!
?
@@ -89,6 +97,9 @@ static GOptionEntry install_option_entries[] = { | |||
{ "touch-if-changed", 0, 0, G_OPTION_ARG_STRING, &opt_touch_if_changed, "Update the modification time on FILE if a new commit was created", "FILE" }, | |||
{ "workdir", 0, 0, G_OPTION_ARG_STRING, &opt_workdir, "Working directory", "WORKDIR" }, | |||
{ "workdir-tmpfs", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_workdir_tmpfs, "Use tmpfs for working state", NULL }, | |||
#ifdef HAVE_RUST | |||
{ "yaml-manifest", 0, 0, G_OPTION_ARG_NONE, &opt_yaml_manifest, "Assume the manifest is in YAML format", NULL }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd vote we do this automatically if the file ends in .yaml
, and require that extension just for simplicity. If we wanted something a bit more robust we could try GLib's MIME sniffing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I see you did this below...kind of uncertain about the need for an option, but eh.)
g_auto(GLnxTmpfile) json_contents = { 0, }; | ||
if (opt_yaml_manifest || | ||
g_str_has_suffix (treefile_path, ".yaml") || | ||
g_str_has_suffix (treefile_path, ".yml")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's detect this outside the conditional, and throw a clear error #ifndef HAVE_RUST
.
return glnx_throw (error, "Failed to load YAML treefile %s", treefile_path); | ||
|
||
/* or just lseek back to 0 and use json_parser_load_from_data here? */ | ||
treefile_path = fdpath = g_strdup_printf ("/proc/self/fd/%d", json_contents.fd); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That won't correctly reference external files like the passwd
and treecompose-post.sh
bits. This is why the current assembler code does the tmpdir/copy dance.
(Shades of Dockerfile
+docker build
+"contextdir" here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It still works because as far as the rest of the code is concerned, the treefile path is still the JSON one. We're only passing the /proc/self
path to json-glib
when parsing.
rust/Cargo.lock
Outdated
@@ -0,0 +1,204 @@ | |||
[[package]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think opinion is split on this somewhat, but I personally don't like shipping Cargo.lock
in git. Do pinning when we hit breakage (and ideally submit patches), rather than the constant maintenance overhead of bumping it.
} | ||
|
||
#[derive(Serialize, Deserialize, Debug)] | ||
pub struct TreeComposeConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is a human format (as YAML suggests) and newly introduced, I'd suggest to version this with a "kind+value" tag as it will make future breaking changes easier to handle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooh that's beautiful. Indeed that'd be useful here. That said the plan is to deprecate this specific functionality entirely in favor of sysusers.
rust/src/treefile.rs
Outdated
fn treefile_read_impl(filename: &Path, output: fs::File) -> io::Result<()> { | ||
let f = fs::File::open(filename)?; | ||
|
||
let mut treefile: TreeComposeConfig = match serde_yaml::from_reader(f) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll likely want to wrap f
in a std::io::BufReader
for serde_yaml.
This still needs some sanity test! |
Now with proper error handling and a test! ⬆️ (And a pass of |
I pushed a fixup to do the same thing as ostreedev/ostree@1f83259 - out of curiosity how are you installing |
Ahh thanks. Odd I wasn't seeing this locally. |
OK what do you think about this: From d0d95645e220ca56e381278c44facbb6ad9009f3 Mon Sep 17 00:00:00 2001
From: Colin Walters <walters@verbum.org>
Date: Mon, 4 Jun 2018 11:32:16 -0400
Subject: [PATCH] wip
---
rust/src/treefile.rs | 53 +++++++++++++++++++++-----------------------
1 file changed, 25 insertions(+), 28 deletions(-)
diff --git a/rust/src/treefile.rs b/rust/src/treefile.rs
index 24fdfd48..9080be98 100644
--- a/rust/src/treefile.rs
+++ b/rust/src/treefile.rs
@@ -12,15 +12,11 @@ extern crate serde;
extern crate serde_json;
extern crate serde_yaml;
-use std::ffi::CStr;
-use std::ffi::CString;
-use std::ffi::OsStr;
-use std::fmt::Display;
-use std::fs;
-use std::io;
+use std::ffi::{CStr, CString, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::FromRawFd;
use std::path::Path;
+use std::{fs, io, ptr};
#[no_mangle]
pub extern "C" fn treefile_read(
@@ -36,10 +32,7 @@ pub extern "C" fn treefile_read(
let c_str: &CStr = unsafe { CStr::from_ptr(filename) };
let filename_path = Path::new(OsStr::from_bytes(c_str.to_bytes()));
- match treefile_read_impl(filename_path, output_file) {
- Err(e) => error.propagate(e),
- _ => 1,
- }
+ treefile_read_impl(filename_path, output_file).map_gerr(error)
}
fn treefile_read_impl(filename: &Path, output: fs::File) -> io::Result<()> {
@@ -75,28 +68,32 @@ fn whitespace_split_packages(pkgs: &Vec<String>) -> Vec<String> {
return ret;
}
-// This is like the Rust -> C version of `g_propagate_error`.
-// https://developer.gnome.org/glib/stable/glib-Error-Reporting.html#g-propagate-error
-trait ErrorPropagate {
- fn propagate<E: Display>(&self, e: E) -> libc::c_int;
+// With this, you can just add .map_gerr(error) at the end of functions that
+// return a Result.
+trait RustToGError {
+ fn map_gerr(self: Self, error: *mut *mut glib_sys::GError) -> libc::c_int;
}
-impl ErrorPropagate for *mut *mut glib_sys::GError {
- fn propagate<E: Display>(&self, e: E) -> libc::c_int {
- if !(*self).is_null() {
- let c_msg = CString::new(format!("{}", e)).unwrap().into_raw();
- unsafe {
- // XXX: should check for overwriting
- **self = glib_sys::g_error_new_literal(
- gio_sys::g_io_error_quark(),
- gio_sys::G_IO_ERROR_FAILED,
- c_msg,
- );
- // recapture and let fall out
- let _ = CString::from_raw(c_msg);
+impl<T, E> RustToGError for Result<T, E>
+where
+ E: std::error::Error,
+{
+ fn map_gerr(self: Result<T, E>, error: *mut *mut glib_sys::GError) -> libc::c_int {
+ match &self {
+ &Ok(_) => 1,
+ &Err(ref e) => {
+ unsafe {
+ assert!(*error == ptr::null_mut());
+ let c_msg = CString::new(e.description()).unwrap().into_raw();
+ *error = glib_sys::g_error_new_literal(
+ gio_sys::g_io_error_quark(),
+ gio_sys::G_IO_ERROR_FAILED,
+ c_msg,
+ )
+ };
+ 0
}
}
- 0 // for convenience as return/final expressions
}
}
--
2.17.0
|
Yeah, that looks nice to me! |
Isn't this simpler? Reference
|
Let's modernize and start supporting YAML treefiles. I'll dare make the sweeping generalization that most people would prefer reading and writing YAML over JSON. This takes bits from coreos-assembler[1] that know how to serialize a YAML file and spit it back out as a JSON and makes it into a shared lib that we can link against. We could use this eventually for JSON inputs as well to force a validation check before composing. If we go this route, we could then turn on `--enable-rust` in FAHC for now and drop the duplicate code in coreos-assembler. [1] https://github.com/cgwalters/coreos-assembler
OK I squashed in that, and changed the commit message to drop the |
Oh yeah, of course. I think I was so focused on trying to use I like the new approach. My only bikeshed would be @lucab mentioned earlier some concerns with double linking, but AFAICT playing with Anyway, I'll let you do the r+! |
I thought about that actually on the drive to lunch. If I had to spell it out, it's really |
WDYT of ⬆️? (I.e. |
Oh man I was literally writing the same thing at the same time! My
|
(I don't have a strong opinion on |
Done! ⬆️ |
Looks like C7 is having rpm-md issues, but let's try this... |
Let's modernize and start supporting YAML treefiles. I'll dare make the sweeping generalization that most people would prefer reading and writing YAML over JSON. This takes bits from coreos-assembler[1] that know how to serialize a YAML file and spit it back out as a JSON and makes it into a shared lib that we can link against. We could use this eventually for JSON inputs as well to force a validation check before composing. If we go this route, we could then turn on `--enable-rust` in FAHC for now and drop the duplicate code in coreos-assembler. [1] https://github.com/cgwalters/coreos-assembler Closes: #1377 Approved by: cgwalters
💔 Test failed - status-atomicjenkins |
@rh-atomic-bot retry |
☀️ Test successful - status-atomicjenkins |
rpm-ostree now includes some Rust code. See coreos/rpm-ostree#1377 and coreos/rpm-ostree#1391
We'd like to use YAML for future rpm-ostree work; among other things it supports comments. See https://pagure.io/fedora-atomic/pull-request/125 and the original coreos/rpm-ostree#1377 Signed-off-by: Colin Walters <walters@verbum.org>
Let's modernize and start supporting YAML treefiles. I'll dare make the
sweeping generalization that most people would prefer reading and
writing YAML over JSON.
This takes bits from coreos-assembler[1] that know how to serialize a
YAML file and spit it back out as a JSON and makes it into a shared lib
that we can link against. We could use this eventually for JSON inputs
as well to force a validation check before composing.
If we go this route, we could then turn on
--enable-rust
in FAHC fornow and drop the duplicate code in coreos-assembler.
[1] https://github.com/cgwalters/coreos-assembler