|
1 | | -use std::{os::unix::prelude::AsRawFd, path::Path}; |
2 | | - |
3 | | -use pin_project::pin_project; |
4 | | -use socket2::Socket; |
5 | | -use tokio::{ |
6 | | - io::{AsyncRead, AsyncWrite}, |
7 | | - net::{UnixListener, UnixStream}, |
8 | | -}; |
9 | | -use tonic::transport::server::Connected; |
10 | | - |
11 | 1 | pub mod address; |
12 | | - |
13 | | -/// Adapter for using [`UnixStream`] as a [`tonic`] connection |
14 | | -/// Tonic usually communicates via TCP sockets, but the Kubernetes CSI interface expects |
15 | | -/// plugins to use Unix sockets instead. |
16 | | -/// This provides a wrapper implementation which delegates to tokio's [`UnixStream`] in order |
17 | | -/// to enable tonic to communicate via Unix sockets. |
18 | | -#[pin_project] |
19 | | -pub struct TonicUnixStream(#[pin] pub UnixStream); |
20 | | - |
21 | | -impl AsyncRead for TonicUnixStream { |
22 | | - fn poll_read( |
23 | | - self: std::pin::Pin<&mut Self>, |
24 | | - cx: &mut std::task::Context<'_>, |
25 | | - buf: &mut tokio::io::ReadBuf<'_>, |
26 | | - ) -> std::task::Poll<std::io::Result<()>> { |
27 | | - self.project().0.poll_read(cx, buf) |
28 | | - } |
29 | | -} |
30 | | - |
31 | | -impl AsyncWrite for TonicUnixStream { |
32 | | - fn poll_write( |
33 | | - self: std::pin::Pin<&mut Self>, |
34 | | - cx: &mut std::task::Context<'_>, |
35 | | - buf: &[u8], |
36 | | - ) -> std::task::Poll<Result<usize, std::io::Error>> { |
37 | | - self.project().0.poll_write(cx, buf) |
38 | | - } |
39 | | - |
40 | | - fn poll_flush( |
41 | | - self: std::pin::Pin<&mut Self>, |
42 | | - cx: &mut std::task::Context<'_>, |
43 | | - ) -> std::task::Poll<Result<(), std::io::Error>> { |
44 | | - self.project().0.poll_flush(cx) |
45 | | - } |
46 | | - |
47 | | - fn poll_shutdown( |
48 | | - self: std::pin::Pin<&mut Self>, |
49 | | - cx: &mut std::task::Context<'_>, |
50 | | - ) -> std::task::Poll<Result<(), std::io::Error>> { |
51 | | - self.project().0.poll_shutdown(cx) |
52 | | - } |
53 | | - |
54 | | - fn poll_write_vectored( |
55 | | - self: std::pin::Pin<&mut Self>, |
56 | | - cx: &mut std::task::Context<'_>, |
57 | | - bufs: &[std::io::IoSlice<'_>], |
58 | | - ) -> std::task::Poll<Result<usize, std::io::Error>> { |
59 | | - self.project().0.poll_write_vectored(cx, bufs) |
60 | | - } |
61 | | - |
62 | | - fn is_write_vectored(&self) -> bool { |
63 | | - self.0.is_write_vectored() |
64 | | - } |
65 | | -} |
66 | | - |
67 | | -impl Connected for TonicUnixStream { |
68 | | - type ConnectInfo = (); |
69 | | - |
70 | | - fn connect_info(&self) -> Self::ConnectInfo {} |
71 | | -} |
72 | | - |
73 | | -/// Bind a Unix Domain Socket listener that is only accessible to the current user |
74 | | -pub fn uds_bind_private(path: impl AsRef<Path>) -> Result<UnixListener, std::io::Error> { |
75 | | - // Workaround for https://github.com/tokio-rs/tokio/issues/4422 |
76 | | - let socket = Socket::new(socket2::Domain::UNIX, socket2::Type::STREAM, None)?; |
77 | | - unsafe { |
78 | | - // Socket-level chmod is propagated to the file created by Socket::bind. |
79 | | - // We need to chmod /before/ creating the file, because otherwise there is a brief window where |
80 | | - // the file is world-accessible (unless restricted by the global umask). |
81 | | - if libc::fchmod(socket.as_raw_fd(), 0o600) == -1 { |
82 | | - return Err(std::io::Error::last_os_error()); |
83 | | - } |
84 | | - } |
85 | | - socket.bind(&socket2::SockAddr::unix(path)?)?; |
86 | | - socket.listen(1024)?; |
87 | | - socket.set_nonblocking(true)?; |
88 | | - UnixListener::from_std(socket.into()) |
89 | | -} |
90 | | - |
91 | | -/// Combines the messages of an error and its sources into a [`String`] of the form `"error: source 1: source 2: root error"` |
92 | | -pub fn error_full_message(err: &dyn std::error::Error) -> String { |
93 | | - use std::fmt::Write; |
94 | | - // Build the full hierarchy of error messages by walking up the stack until an error |
95 | | - // without `source` set is encountered and concatenating all encountered error strings. |
96 | | - let mut full_msg = format!("{}", err); |
97 | | - let mut curr_err = err.source(); |
98 | | - while let Some(curr_source) = curr_err { |
99 | | - write!(full_msg, ": {curr_source}").expect("string formatting should be infallible"); |
100 | | - curr_err = curr_source.source(); |
101 | | - } |
102 | | - full_msg |
103 | | -} |
104 | | - |
105 | | -#[cfg(test)] |
106 | | -mod tests { |
107 | | - use crate::utils::error_full_message; |
108 | | - |
109 | | - #[test] |
110 | | - fn error_messages() { |
111 | | - assert_eq!( |
112 | | - error_full_message(anyhow::anyhow!("standalone error").as_ref()), |
113 | | - "standalone error" |
114 | | - ); |
115 | | - assert_eq!( |
116 | | - error_full_message( |
117 | | - anyhow::anyhow!("root error") |
118 | | - .context("middleware") |
119 | | - .context("leaf") |
120 | | - .as_ref() |
121 | | - ), |
122 | | - "leaf: middleware: root error" |
123 | | - ); |
124 | | - } |
125 | | -} |
| 2 | +pub mod error; |
| 3 | +pub mod unix_stream; |
0 commit comments