LCOV - differential code coverage report
Current view: top level - proxy/src/console - messages.rs (source / functions) Coverage Total Hit UBC CBC
Current: f6946e90941b557c917ac98cd5a7e9506d180f3e.info Lines: 93.6 % 94 88 6 88
Current Date: 2023-10-19 02:04:12 Functions: 35.1 % 94 33 61 33
Baseline: c8637f37369098875162f194f92736355783b050.info
Baseline Date: 2023-10-18 20:25:20

           TLA  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 UBC           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 CBC          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              68 : #[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              60 :     pub fn traffic_labels(&self, direction: &'static str) -> [&str; 4] {
     113              60 :         [
     114              60 :             direction,
     115              60 :             &self.project_id,
     116              60 :             &self.endpoint_id,
     117              60 :             &self.branch_id,
     118              60 :         ]
     119              60 :     }
     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