-
Notifications
You must be signed in to change notification settings - Fork 86
runc client #22
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
runc client #22
Conversation
Signed-off-by: Yuna Tomida <ytomida.mmm@gmail.com>
|
|
And updated MSRV to 1.54 for using |
Signed-off-by: Yuna Tomida <ytomida.mmm@gmail.com>
Signed-off-by: Yuna Tomida <ytomida.mmm@gmail.com>
|
LGTM, but needs to fix the format error https://github.com/containerd/rust-extensions/runs/5048032832?check_suite_focus=true Thanks! |
Signed-off-by: Yuna Tomida <ytomida.mmm@gmail.com>
Yes, in a separate commit 🙏 Thanks |
Signed-off-by: Yuna Tomida <ytomida.mmm@gmail.com>
I think this was introduced after updating rust to 1.54 in rust-toolchain.toml |
Cargo.toml
Outdated
| "crates/shim", | ||
| "crates/snapshots" | ||
| "crates/snapshots", | ||
| "crates/runc-rust" |
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.
just crates/runc ?
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.
+1
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.
Ok, thank you!
crates/runc-rust/README.md
Outdated
| Note that async client depends on [tokio](https://github.com/tokio-rs/tokio), then please use it on tokio runtime. | ||
|
|
||
| ```rust | ||
| use runc_rust as runc; |
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.
use runc
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.
Ok, thank you!
crates/runc-rust/src/io.rs
Outdated
| stderr: Option<Pipe>, | ||
| } | ||
|
|
||
| impl RuncPipedIO { |
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 we could omit these Runc prefixes everywhere.
runc::RuncPipedIO -> runc::PipedIo
Same for other structs.
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.
Ok, thank you!
crates/runc-rust/src/lib.rs
Outdated
| serde_json::to_string(resources).map_err(Error::JsonDeserializationFailed)?; | ||
| f.write(spec_json.as_bytes()) | ||
| .map_err(Error::SpecFileCreationError)?; | ||
| f.flush().map_err(Error::SpecFileCreationError)?; |
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.
This block can be reduced to:
fs::write(path, spec_json)?;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.
Ok, thank you!
crates/runc-rust/src/container.rs
Outdated
|
|
||
| #[test] | ||
| fn serde_test() { | ||
| let j = " |
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.
How about using raw string literals here? So that you can write " without escaping.
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's good, thank you!
crates/runc-rust/src/container.rs
Outdated
| }"; | ||
|
|
||
| let c: Container = serde_json::from_str(j).unwrap(); | ||
| println!("{:#?}", c); |
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.
Personally prefer to keep stdout clean. If you have some properties you want to check, write them as tests.
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.
Thank you for recommend but I'm sorry, I couldn't see your point.
This function is in mod tests and attributed as #[test], but it's not enough?
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.
What you'd like to do with the println! output? If you want to sometimes manually check the result, it would be better to put more context or use dbg!.
That being said, it would be great if you can automate the manual checking part by writing assertions.
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.
Oh, I see, thank you!
I'll rewrite them with assertions.
crates/runc-rust/src/io.rs
Outdated
| fn close(&self) { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| /// Set IO for passed command. | ||
| /// Read side of stdin, write side of stdout and write side of stderr should be provided to command. | ||
| fn set(&self, _cmd: &mut Command) -> std::io::Result<()> { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| // tokio version of set() | ||
| fn set_tk(&self, _cmd: &mut tokio::process::Command) -> std::io::Result<()> { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn close_after_start(&self) { | ||
| unimplemented!() | ||
| } |
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.
Not clear about these unimplemented. Due to time constraints?
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' ll remove them, thank you!
| pub fn take_read(&self) -> Option<File> { | ||
| let mut m = self.rd.lock().unwrap(); | ||
| m.take() | ||
| } | ||
|
|
||
| pub fn take_write(&self) -> Option<File> { | ||
| let mut m = self.wr.lock().unwrap(); | ||
| m.take() | ||
| } | ||
|
|
||
| pub fn close_read(&self) { | ||
| let mut m = self.rd.lock().unwrap(); | ||
| let _ = m.take(); | ||
| } | ||
|
|
||
| pub fn close_write(&self) { | ||
| let mut m = self.wr.lock().unwrap(); | ||
| let _ = m.take(); | ||
| } |
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.
lock() returns Result. Does it make sense to return the errors rather than panic?
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.
not in case of std Mutex - https://users.rust-lang.org/t/should-i-unwrap-a-mutex-lock/61519
crates/runc-rust/src/io.rs
Outdated
| } | ||
|
|
||
| impl RuncPipedIO { | ||
| pub fn new(uid: isize, gid: isize, opts: IOOption) -> std::io::Result<Self> { |
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.
Should this use isize 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.
Actually, I just followed Go's but it seems to be unnecessary...
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.
Thank you, I'll change it to u32
crates/runc-rust/src/io.rs
Outdated
| } | ||
|
|
||
| fn close_after_start(&self) { | ||
| unimplemented!() |
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.
unimplemented is not necessary here, just have it as
fn close_after_start(&self);so the compiler will require implementation at compile time when implementing this trait.
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.
Ok, thank you!
| /// when one side of closed, this struct represent it with [`None`] | ||
| #[derive(Debug)] | ||
| pub struct Pipe { | ||
| // Might be ugly hack: using mutex in order to take rd/wr under immutable [`Pipe`] |
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.
Is there a particular reason why it can't be mutable?
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.
Actually what I want to do is accessing struct containing Pipe like PipeIO here from multipile thread, and extract file, and copy the stdio to other io entry from higher layer(e.g. Fifo that passed from containerd to shim).
In such case, we should Arc it to reference from multiple thread and need Mutex to be mutable(to read/write), but any two threads will not access the same entry(e.g. stdout and stdout) and I thought it's redundant to make Mutex<PipedIO>.
Is there any other good way to do this?
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.
Also, like close_after_write, I want to close some files(but not all) manually from threads.
crates/runc-rust/src/io.rs
Outdated
| None | ||
| } | ||
|
|
||
| fn close(&self) { |
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.
impl Drop instead?
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.
Ok, thank you!
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.
As destructor automatically drop files inside structs, I will just erase this trait method.
| pub fn take_read(&self) -> Option<File> { | ||
| let mut m = self.rd.lock().unwrap(); | ||
| m.take() | ||
| } | ||
|
|
||
| pub fn take_write(&self) -> Option<File> { | ||
| let mut m = self.wr.lock().unwrap(); | ||
| m.take() | ||
| } | ||
|
|
||
| pub fn close_read(&self) { | ||
| let mut m = self.rd.lock().unwrap(); | ||
| let _ = m.take(); | ||
| } | ||
|
|
||
| pub fn close_write(&self) { | ||
| let mut m = self.wr.lock().unwrap(); | ||
| let _ = m.take(); | ||
| } |
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.
not in case of std Mutex - https://users.rust-lang.org/t/should-i-unwrap-a-mutex-lock/61519
Signed-off-by: Yuna Tomida <ytomida.mmm@gmail.com>
Signed-off-by: Yuna Tomida <ytomida.mmm@gmail.com>
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
228fc2b to
6e17be8
Compare
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
AkihiroSuda
left a comment
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.
CI green.
Let's merge this toward reopening the actual shim part (#21)
|
@AkihiroSuda do we need 6e17be8 for this PR? Seems like |
|
@kzys PTAL |
Cargo automatically bumped up them, so I committed. |
| - run: cargo fmt --all -- --check --files-with-diff | ||
| - run: cargo clippy --all-targets --all-features -- -D warnings | ||
| - run: cargo test --all-features | ||
| # runc::tests::test_exec needs $XDG_RUNTIME_DIR to be set |
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.
Shouldn't the test set the environment variable if so?
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.
No, usually set by pam_systemd (which is missing in GHA)
| #[derive(Debug, Serialize, Deserialize)] | ||
| pub struct Container { | ||
| pub id: String, | ||
| pub pid: usize, |
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.
u32? Response's pid is u32.
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's right, thank you!
@AkihiroSuda can we revert it? Seems like its not relevant for this PR (also looks like it depends on environment, I'll TAL later). |
kzys
left a comment
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.
Mostly nits. I'm fine merging the PR as is and address them later.
| let stdout = String::from_utf8(result.stdout).unwrap(); | ||
| let stderr = String::from_utf8(result.stderr).unwrap(); |
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.
Wouldn't it fail if the command outputs some non UTF-8 bytes (e.g. gzip)?
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.
Sure. However which is preferable in such case, returning parse error or return the raw value to caller?
This is just my personal opinion but I think returning error is better. Higher level wrappers which use this crate, like shim, will bend the stdout/err of command to IO that they prepare in advance. And they can handle such non-utf8 within their procedure if needed.
In such situation, these result.stdout and result.stderr will be empty string (and never be non-utf8).
| let args = ["list".to_string(), "--format-json".to_string()]; | ||
| let res = self.launch(self.command(&args)?, true).await?; | ||
| let output = res.output.trim(); | ||
| // Ugly hack to work around golang |
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 want to know more about the thing you need to workaround :)
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.
Actually, this is for just handling null json. But I think this way (with comparering output and "null") is not concise...
Is it comprehensive if I change it like below?
Ok(serde_json::from_str::<Option<Vec<Container>>>(output).map_err(/*omit*/)?.unwrap_or_default())|
|
||
| fn dummy_process() -> Process { | ||
| serde_json::from_str( | ||
| " |
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.
raw string?
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.
Sure. Thank you!
Not depending on local env, simply 2.27.1 is the latest ( https://crates.io/crates/protobuf ), and Cargo picks up the latest one because we do not commit Cargo.lock in the repo (why?) |
We generally don't commit Cargo.lock for libraries: https://doc.rust-lang.org/cargo/faq.html#why-do-binaries-have-cargolock-in-version-control-but-not-libraries |
| pub struct Container { | ||
| pub id: String, | ||
| pub pid: usize, | ||
| pub status: String, |
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.
|
|
||
| /// Return the state of a container | ||
| pub fn state(&self, id: &str) -> Result<Container> { | ||
| let args = ["state".to_string(), id.to_string()]; |
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 seems that containerd-shim-runc-v2(Go version) uses its memory variable to strore container state(eg. https://github.com/containerd/containerd/blob/release%2F1.5/pkg/process/init_state.go) instead of runc state cli command, cause shim has its own state system maybe.
| ]; | ||
| self.launch(self.command(&args)?, true)?; | ||
| Ok(()) | ||
| } |
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.
update normaly takes stdin as arg, we should provide this way for compatibility
|
Ok, I'm going to merge this in. Let's address remaining items in follow up PRs. |

runc client
Signed-off-by: Yuna Tomida ytomida.mmm@gmail.com