LCOV - code coverage report
Current view: top level - proxy/src/console - messages.rs (source / functions) Coverage Total Hit
Test: 8ac049b474321fdc72ddcb56d7165153a1a900e8.info Lines: 93.6 % 94 88
Test Date: 2023-09-06 10:18:01 Functions: 35.1 % 94 33

            Line data    Source code
       1              : use serde::Deserialize;
       2              : use std::fmt;
       3              : 
       4              : /// Generic error response with human-readable description.
       5              : /// Note that we can't always present it to user as is.
       6            0 : #[derive(Debug, Deserialize)]
       7              : pub struct ConsoleError {
       8              :     pub error: Box<str>,
       9              : }
      10              : 
      11              : /// Response which holds client's auth secret, e.g. [`crate::scram::ServerSecret`].
      12              : /// Returned by the `/proxy_get_role_secret` API method.
      13            0 : #[derive(Deserialize)]
      14              : pub struct GetRoleSecret {
      15              :     pub role_secret: Box<str>,
      16              : }
      17              : 
      18              : // Manually implement debug to omit sensitive info.
      19              : impl fmt::Debug for GetRoleSecret {
      20            0 :     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
      21            0 :         f.debug_struct("GetRoleSecret").finish_non_exhaustive()
      22            0 :     }
      23              : }
      24              : 
      25              : /// Response which holds compute node's `host:port` pair.
      26              : /// Returned by the `/proxy_wake_compute` API method.
      27            0 : #[derive(Debug, Deserialize)]
      28              : pub struct WakeCompute {
      29              :     pub address: Box<str>,
      30              :     pub aux: MetricsAuxInfo,
      31              : }
      32              : 
      33              : /// Async response which concludes the link auth flow.
      34              : /// Also known as `kickResponse` in the console.
      35           24 : #[derive(Debug, Deserialize)]
      36              : pub struct KickSession<'a> {
      37              :     /// Session ID is assigned by the proxy.
      38              :     pub session_id: &'a str,
      39              : 
      40              :     /// Compute node connection params.
      41              :     #[serde(deserialize_with = "KickSession::parse_db_info")]
      42              :     pub result: DatabaseInfo,
      43              : }
      44              : 
      45              : impl KickSession<'_> {
      46            4 :     fn parse_db_info<'de, D>(des: D) -> Result<DatabaseInfo, D::Error>
      47            4 :     where
      48            4 :         D: serde::Deserializer<'de>,
      49            4 :     {
      50            8 :         #[derive(Deserialize)]
      51            4 :         enum Wrapper {
      52            4 :             // Currently, console only reports `Success`.
      53            4 :             // `Failure(String)` used to be here... RIP.
      54            4 :             Success(DatabaseInfo),
      55            4 :         }
      56            4 : 
      57            4 :         Wrapper::deserialize(des).map(|x| match x {
      58            4 :             Wrapper::Success(info) => info,
      59            4 :         })
      60            4 :     }
      61              : }
      62              : 
      63              : /// Compute node connection params.
      64           83 : #[derive(Deserialize)]
      65              : pub struct DatabaseInfo {
      66              :     pub host: Box<str>,
      67              :     pub port: u16,
      68              :     pub dbname: Box<str>,
      69              :     pub user: Box<str>,
      70              :     /// Console always provides a password, but it might
      71              :     /// be inconvenient for debug with local PG instance.
      72              :     pub password: Option<Box<str>>,
      73              :     pub aux: MetricsAuxInfo,
      74              : }
      75              : 
      76              : // Manually implement debug to omit sensitive info.
      77              : impl fmt::Debug for DatabaseInfo {
      78            6 :     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
      79            6 :         f.debug_struct("DatabaseInfo")
      80            6 :             .field("host", &self.host)
      81            6 :             .field("port", &self.port)
      82            6 :             .field("dbname", &self.dbname)
      83            6 :             .field("user", &self.user)
      84            6 :             .finish_non_exhaustive()
      85            6 :     }
      86              : }
      87              : 
      88              : /// Various labels for prometheus metrics.
      89              : /// Also known as `ProxyMetricsAuxInfo` in the console.
      90           59 : #[derive(Debug, Deserialize, Default)]
      91              : pub struct MetricsAuxInfo {
      92              :     pub endpoint_id: Box<str>,
      93              :     pub project_id: Box<str>,
      94              :     pub branch_id: Box<str>,
      95              : }
      96              : 
      97              : impl MetricsAuxInfo {
      98              :     /// Definitions of labels for traffic metric.
      99              :     pub const TRAFFIC_LABELS: &'static [&'static str] = &[
     100              :         // Received (rx) / sent (tx).
     101              :         "direction",
     102              :         // ID of a project.
     103              :         "project_id",
     104              :         // ID of an endpoint within a project.
     105              :         "endpoint_id",
     106              :         // ID of a branch within a project (snapshot).
     107              :         "branch_id",
     108              :     ];
     109              : 
     110              :     /// Values of labels for traffic metric.
     111              :     // TODO: add more type safety (validate arity & positions).
     112           56 :     pub fn traffic_labels(&self, direction: &'static str) -> [&str; 4] {
     113           56 :         [
     114           56 :             direction,
     115           56 :             &self.project_id,
     116           56 :             &self.endpoint_id,
     117           56 :             &self.branch_id,
     118           56 :         ]
     119           56 :     }
     120              : }
     121              : 
     122              : #[cfg(test)]
     123              : mod tests {
     124              :     use super::*;
     125              :     use serde_json::json;
     126              : 
     127            4 :     fn dummy_aux() -> serde_json::Value {
     128            4 :         json!({
     129            4 :             "endpoint_id": "endpoint",
     130            4 :             "project_id": "project",
     131            4 :             "branch_id": "branch",
     132            4 :         })
     133            4 :     }
     134              : 
     135            1 :     #[test]
     136            1 :     fn parse_kick_session() -> anyhow::Result<()> {
     137            1 :         // This is what the console's kickResponse looks like.
     138            1 :         let json = json!({
     139            1 :             "session_id": "deadbeef",
     140            1 :             "result": {
     141            1 :                 "Success": {
     142            1 :                     "host": "localhost",
     143            1 :                     "port": 5432,
     144            1 :                     "dbname": "postgres",
     145            1 :                     "user": "john_doe",
     146            1 :                     "password": "password",
     147            1 :                     "aux": dummy_aux(),
     148            1 :                 }
     149            1 :             }
     150            1 :         });
     151            1 :         let _: KickSession = serde_json::from_str(&json.to_string())?;
     152              : 
     153            1 :         Ok(())
     154            1 :     }
     155              : 
     156            1 :     #[test]
     157            1 :     fn parse_db_info() -> anyhow::Result<()> {
     158              :         // with password
     159            1 :         let _: DatabaseInfo = serde_json::from_value(json!({
     160            1 :             "host": "localhost",
     161            1 :             "port": 5432,
     162            1 :             "dbname": "postgres",
     163            1 :             "user": "john_doe",
     164            1 :             "password": "password",
     165            1 :             "aux": dummy_aux(),
     166            1 :         }))?;
     167              : 
     168              :         // without password
     169            1 :         let _: DatabaseInfo = serde_json::from_value(json!({
     170            1 :             "host": "localhost",
     171            1 :             "port": 5432,
     172            1 :             "dbname": "postgres",
     173            1 :             "user": "john_doe",
     174            1 :             "aux": dummy_aux(),
     175            1 :         }))?;
     176              : 
     177              :         // new field (forward compatibility)
     178            1 :         let _: DatabaseInfo = serde_json::from_value(json!({
     179            1 :             "host": "localhost",
     180            1 :             "port": 5432,
     181            1 :             "dbname": "postgres",
     182            1 :             "user": "john_doe",
     183            1 :             "project": "hello_world",
     184            1 :             "N.E.W": "forward compatibility check",
     185            1 :             "aux": dummy_aux(),
     186            1 :         }))?;
     187              : 
     188            1 :         Ok(())
     189            1 :     }
     190              : }
        

Generated by: LCOV version 2.1-beta