LCOV - code coverage report
Current view: top level - proxy/src/console - messages.rs (source / functions) Coverage Total Hit
Test: 32f4a56327bc9da697706839ed4836b2a00a408f.info Lines: 96.5 % 115 111
Test Date: 2024-02-07 07:37:29 Functions: 44.3 % 106 47

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

Generated by: LCOV version 2.1-beta