fcnetd_client/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::path::Path;
#[cfg(feature = "deadpool")]
use std::path::PathBuf;

use fcnet_types::{FirecrackerNetwork, FirecrackerNetworkOperation};
use serde::Serialize;
use tokio::{
    io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
    net::UnixStream,
};

const OK_RESPONSE: &str = "OK";

#[derive(Debug, thiserror::Error)]
pub enum FcnetdError {
    #[error("Writing the request to the connection failed: {0}")]
    RequestWriteError(std::io::Error),
    #[error("Serializing the request to JSON failed: {0}")]
    RequestSerializeError(serde_json::Error),
    #[error("Reading the response from the connection failed: {0}")]
    ResponseReadError(std::io::Error),
    #[error("The connection was closed before a response could be received")]
    ConnectionClosed,
    #[error("The daemon returned a failure of the requested operation: {0}")]
    OperationFailed(String),
}

#[derive(Debug)]
pub struct FcnetdConnection {
    stream: UnixStream,
}

#[derive(Serialize)]
struct Request<'net> {
    operation: FirecrackerNetworkOperation,
    network: &'net FirecrackerNetwork,
}

#[derive(Debug)]
#[cfg(feature = "connection-pool")]
pub struct FcnetdConnectionPool {
    path: PathBuf,
    password: Option<String>,
}

#[cfg(feature = "connection-pool")]
impl FcnetdConnectionPool {
    pub fn new(path: impl Into<PathBuf>) -> Self {
        Self {
            path: path.into(),
            password: None,
        }
    }

    pub fn new_with_password(path: impl Into<PathBuf>, password: impl Into<String>) -> Self {
        Self {
            path: path.into(),
            password: Some(password.into()),
        }
    }
}

#[cfg(feature = "deadpool")]
impl deadpool::managed::Manager for FcnetdConnectionPool {
    type Type = FcnetdConnection;

    type Error = std::io::Error;

    async fn create(&self) -> Result<Self::Type, Self::Error> {
        match self.password {
            Some(ref password) => FcnetdConnection::connect_with_password(&self.path, password).await,
            None => FcnetdConnection::connect(&self.path).await,
        }
    }

    async fn recycle(
        &self,
        _obj: &mut Self::Type,
        _metrics: &deadpool::managed::Metrics,
    ) -> deadpool::managed::RecycleResult<Self::Error> {
        deadpool::managed::RecycleResult::Ok(())
    }
}

impl FcnetdConnection {
    pub async fn connect(path: impl AsRef<Path>) -> Result<Self, std::io::Error> {
        let stream = UnixStream::connect(path).await?;
        Ok(Self { stream })
    }

    pub async fn connect_with_password(path: impl AsRef<Path>, password: impl Into<String>) -> Result<Self, std::io::Error> {
        let password = password.into();
        let mut stream = UnixStream::connect(path).await?;
        stream.write_all(format!("{password}\n").as_bytes()).await?;
        Ok(Self { stream })
    }

    pub async fn run(&mut self, network: &FirecrackerNetwork, operation: FirecrackerNetworkOperation) -> Result<(), FcnetdError> {
        let request = Request { operation, network };
        let request_json = serde_json::to_string(&request).map_err(FcnetdError::RequestSerializeError)?;
        self.stream
            .write_all(format!("{request_json}\n").as_bytes())
            .await
            .map_err(FcnetdError::RequestWriteError)?;

        let mut response_reader = BufReader::new(&mut self.stream).lines();
        let response = match response_reader.next_line().await {
            Ok(Some(response)) => response,
            Ok(None) => return Err(FcnetdError::ConnectionClosed),
            Err(err) => return Err(FcnetdError::ResponseReadError(err)),
        };

        if response != OK_RESPONSE {
            return Err(FcnetdError::OperationFailed(response));
        }

        Ok(())
    }
}