LCOV - code coverage report
Current view: top level - compute_tools/src/http - api.rs (source / functions) Coverage Total Hit
Test: 6fa910d1c9aea142e54ede6987809ef55544c500.info Lines: 0.0 % 417 0
Test Date: 2024-11-19 23:07:42 Functions: 0.0 % 24 0

            Line data    Source code
       1              : use std::convert::Infallible;
       2              : use std::net::IpAddr;
       3              : use std::net::Ipv6Addr;
       4              : use std::net::SocketAddr;
       5              : use std::sync::Arc;
       6              : use std::thread;
       7              : 
       8              : use crate::catalog::SchemaDumpError;
       9              : use crate::catalog::{get_database_schema, get_dbs_and_roles};
      10              : use crate::compute::forward_termination_signal;
      11              : use crate::compute::{ComputeNode, ComputeState, ParsedSpec};
      12              : use crate::installed_extensions;
      13              : use compute_api::requests::{ConfigurationRequest, ExtensionInstallRequest, SetRoleGrantsRequest};
      14              : use compute_api::responses::{
      15              :     ComputeStatus, ComputeStatusResponse, ExtensionInstallResult, GenericAPIError,
      16              :     SetRoleGrantsResponse,
      17              : };
      18              : 
      19              : use anyhow::Result;
      20              : use hyper::header::CONTENT_TYPE;
      21              : use hyper::service::{make_service_fn, service_fn};
      22              : use hyper::{Body, Method, Request, Response, Server, StatusCode};
      23              : use metrics::Encoder;
      24              : use metrics::TextEncoder;
      25              : use tokio::task;
      26              : use tokio_util::sync::CancellationToken;
      27              : use tracing::{debug, error, info, warn};
      28              : use tracing_utils::http::OtelName;
      29              : use utils::failpoint_support::failpoints_handler;
      30              : use utils::http::error::ApiError;
      31              : use utils::http::request::must_get_query_param;
      32              : 
      33            0 : fn status_response_from_state(state: &ComputeState) -> ComputeStatusResponse {
      34            0 :     ComputeStatusResponse {
      35            0 :         start_time: state.start_time,
      36            0 :         tenant: state
      37            0 :             .pspec
      38            0 :             .as_ref()
      39            0 :             .map(|pspec| pspec.tenant_id.to_string()),
      40            0 :         timeline: state
      41            0 :             .pspec
      42            0 :             .as_ref()
      43            0 :             .map(|pspec| pspec.timeline_id.to_string()),
      44            0 :         status: state.status,
      45            0 :         last_active: state.last_active,
      46            0 :         error: state.error.clone(),
      47            0 :     }
      48            0 : }
      49              : 
      50              : // Service function to handle all available routes.
      51            0 : async fn routes(req: Request<Body>, compute: &Arc<ComputeNode>) -> Response<Body> {
      52            0 :     //
      53            0 :     // NOTE: The URI path is currently included in traces. That's OK because
      54            0 :     // it doesn't contain any variable parts or sensitive information. But
      55            0 :     // please keep that in mind if you change the routing here.
      56            0 :     //
      57            0 :     match (req.method(), req.uri().path()) {
      58              :         // Serialized compute state.
      59            0 :         (&Method::GET, "/status") => {
      60            0 :             debug!("serving /status GET request");
      61            0 :             let state = compute.state.lock().unwrap();
      62            0 :             let status_response = status_response_from_state(&state);
      63            0 :             Response::new(Body::from(serde_json::to_string(&status_response).unwrap()))
      64              :         }
      65              : 
      66              :         // Startup metrics in JSON format. Keep /metrics reserved for a possible
      67              :         // future use for Prometheus metrics format.
      68            0 :         (&Method::GET, "/metrics.json") => {
      69            0 :             info!("serving /metrics.json GET request");
      70            0 :             let metrics = compute.state.lock().unwrap().metrics.clone();
      71            0 :             Response::new(Body::from(serde_json::to_string(&metrics).unwrap()))
      72              :         }
      73              : 
      74              :         // Prometheus metrics
      75            0 :         (&Method::GET, "/metrics") => {
      76            0 :             debug!("serving /metrics GET request");
      77              : 
      78            0 :             let mut buffer = vec![];
      79            0 :             let metrics = installed_extensions::collect();
      80            0 :             let encoder = TextEncoder::new();
      81            0 :             encoder.encode(&metrics, &mut buffer).unwrap();
      82            0 : 
      83            0 :             match Response::builder()
      84            0 :                 .status(StatusCode::OK)
      85            0 :                 .header(CONTENT_TYPE, encoder.format_type())
      86            0 :                 .body(Body::from(buffer))
      87              :             {
      88            0 :                 Ok(response) => response,
      89            0 :                 Err(err) => {
      90            0 :                     let msg = format!("error handling /metrics request: {err}");
      91            0 :                     error!(msg);
      92            0 :                     render_json_error(&msg, StatusCode::INTERNAL_SERVER_ERROR)
      93              :                 }
      94              :             }
      95              :         }
      96              :         // Collect Postgres current usage insights
      97            0 :         (&Method::GET, "/insights") => {
      98            0 :             info!("serving /insights GET request");
      99            0 :             let status = compute.get_status();
     100            0 :             if status != ComputeStatus::Running {
     101            0 :                 let msg = format!("compute is not running, current status: {:?}", status);
     102            0 :                 error!(msg);
     103            0 :                 return Response::new(Body::from(msg));
     104            0 :             }
     105              : 
     106            0 :             let insights = compute.collect_insights().await;
     107            0 :             Response::new(Body::from(insights))
     108              :         }
     109              : 
     110            0 :         (&Method::POST, "/check_writability") => {
     111            0 :             info!("serving /check_writability POST request");
     112            0 :             let status = compute.get_status();
     113            0 :             if status != ComputeStatus::Running {
     114            0 :                 let msg = format!(
     115            0 :                     "invalid compute status for check_writability request: {:?}",
     116            0 :                     status
     117            0 :                 );
     118            0 :                 error!(msg);
     119            0 :                 return Response::new(Body::from(msg));
     120            0 :             }
     121              : 
     122            0 :             let res = crate::checker::check_writability(compute).await;
     123            0 :             match res {
     124            0 :                 Ok(_) => Response::new(Body::from("true")),
     125            0 :                 Err(e) => {
     126            0 :                     error!("check_writability failed: {}", e);
     127            0 :                     Response::new(Body::from(e.to_string()))
     128              :                 }
     129              :             }
     130              :         }
     131              : 
     132            0 :         (&Method::POST, "/extensions") => {
     133            0 :             info!("serving /extensions POST request");
     134            0 :             let status = compute.get_status();
     135            0 :             if status != ComputeStatus::Running {
     136            0 :                 let msg = format!(
     137            0 :                     "invalid compute status for extensions request: {:?}",
     138            0 :                     status
     139            0 :                 );
     140            0 :                 error!(msg);
     141            0 :                 return render_json_error(&msg, StatusCode::PRECONDITION_FAILED);
     142            0 :             }
     143              : 
     144            0 :             let request = hyper::body::to_bytes(req.into_body()).await.unwrap();
     145            0 :             let request = serde_json::from_slice::<ExtensionInstallRequest>(&request).unwrap();
     146            0 :             let res = compute
     147            0 :                 .install_extension(&request.extension, &request.database, request.version)
     148            0 :                 .await;
     149            0 :             match res {
     150            0 :                 Ok(version) => render_json(Body::from(
     151            0 :                     serde_json::to_string(&ExtensionInstallResult {
     152            0 :                         extension: request.extension,
     153            0 :                         version,
     154            0 :                     })
     155            0 :                     .unwrap(),
     156            0 :                 )),
     157            0 :                 Err(e) => {
     158            0 :                     error!("install_extension failed: {}", e);
     159            0 :                     render_json_error(&e.to_string(), StatusCode::INTERNAL_SERVER_ERROR)
     160              :                 }
     161              :             }
     162              :         }
     163              : 
     164            0 :         (&Method::GET, "/info") => {
     165            0 :             let num_cpus = num_cpus::get_physical();
     166            0 :             info!("serving /info GET request. num_cpus: {}", num_cpus);
     167            0 :             Response::new(Body::from(
     168            0 :                 serde_json::json!({
     169            0 :                     "num_cpus": num_cpus,
     170            0 :                 })
     171            0 :                 .to_string(),
     172            0 :             ))
     173              :         }
     174              : 
     175              :         // Accept spec in JSON format and request compute configuration. If
     176              :         // anything goes wrong after we set the compute status to `ConfigurationPending`
     177              :         // and update compute state with new spec, we basically leave compute
     178              :         // in the potentially wrong state. That said, it's control-plane's
     179              :         // responsibility to watch compute state after reconfiguration request
     180              :         // and to clean restart in case of errors.
     181            0 :         (&Method::POST, "/configure") => {
     182            0 :             info!("serving /configure POST request");
     183            0 :             match handle_configure_request(req, compute).await {
     184            0 :                 Ok(msg) => Response::new(Body::from(msg)),
     185            0 :                 Err((msg, code)) => {
     186            0 :                     error!("error handling /configure request: {msg}");
     187            0 :                     render_json_error(&msg, code)
     188              :                 }
     189              :             }
     190              :         }
     191              : 
     192            0 :         (&Method::POST, "/terminate") => {
     193            0 :             info!("serving /terminate POST request");
     194            0 :             match handle_terminate_request(compute).await {
     195            0 :                 Ok(()) => Response::new(Body::empty()),
     196            0 :                 Err((msg, code)) => {
     197            0 :                     error!("error handling /terminate request: {msg}");
     198            0 :                     render_json_error(&msg, code)
     199              :                 }
     200              :             }
     201              :         }
     202              : 
     203            0 :         (&Method::GET, "/dbs_and_roles") => {
     204            0 :             info!("serving /dbs_and_roles GET request",);
     205            0 :             match get_dbs_and_roles(compute).await {
     206            0 :                 Ok(res) => render_json(Body::from(serde_json::to_string(&res).unwrap())),
     207              :                 Err(_) => {
     208            0 :                     render_json_error("can't get dbs and roles", StatusCode::INTERNAL_SERVER_ERROR)
     209              :                 }
     210              :             }
     211              :         }
     212              : 
     213            0 :         (&Method::GET, "/database_schema") => {
     214            0 :             let database = match must_get_query_param(&req, "database") {
     215            0 :                 Err(e) => return e.into_response(),
     216            0 :                 Ok(database) => database,
     217            0 :             };
     218            0 :             info!("serving /database_schema GET request with database: {database}",);
     219            0 :             match get_database_schema(compute, &database).await {
     220            0 :                 Ok(res) => render_plain(Body::wrap_stream(res)),
     221              :                 Err(SchemaDumpError::DatabaseDoesNotExist) => {
     222            0 :                     render_json_error("database does not exist", StatusCode::NOT_FOUND)
     223              :                 }
     224            0 :                 Err(e) => {
     225            0 :                     error!("can't get schema dump: {}", e);
     226            0 :                     render_json_error("can't get schema dump", StatusCode::INTERNAL_SERVER_ERROR)
     227              :                 }
     228              :             }
     229              :         }
     230              : 
     231            0 :         (&Method::POST, "/grants") => {
     232            0 :             info!("serving /grants POST request");
     233            0 :             let status = compute.get_status();
     234            0 :             if status != ComputeStatus::Running {
     235            0 :                 let msg = format!(
     236            0 :                     "invalid compute status for set_role_grants request: {:?}",
     237            0 :                     status
     238            0 :                 );
     239            0 :                 error!(msg);
     240            0 :                 return render_json_error(&msg, StatusCode::PRECONDITION_FAILED);
     241            0 :             }
     242              : 
     243            0 :             let request = hyper::body::to_bytes(req.into_body()).await.unwrap();
     244            0 :             let request = serde_json::from_slice::<SetRoleGrantsRequest>(&request).unwrap();
     245              : 
     246            0 :             let res = compute
     247            0 :                 .set_role_grants(
     248            0 :                     &request.database,
     249            0 :                     &request.schema,
     250            0 :                     &request.privileges,
     251            0 :                     &request.role,
     252            0 :                 )
     253            0 :                 .await;
     254            0 :             match res {
     255            0 :                 Ok(()) => render_json(Body::from(
     256            0 :                     serde_json::to_string(&SetRoleGrantsResponse {
     257            0 :                         database: request.database,
     258            0 :                         schema: request.schema,
     259            0 :                         role: request.role,
     260            0 :                         privileges: request.privileges,
     261            0 :                     })
     262            0 :                     .unwrap(),
     263            0 :                 )),
     264            0 :                 Err(e) => render_json_error(
     265            0 :                     &format!("could not grant role privileges to the schema: {e}"),
     266            0 :                     // TODO: can we filter on role/schema not found errors
     267            0 :                     // and return appropriate error code?
     268            0 :                     StatusCode::INTERNAL_SERVER_ERROR,
     269            0 :                 ),
     270              :             }
     271              :         }
     272              : 
     273              :         // get the list of installed extensions
     274              :         // currently only used in python tests
     275              :         // TODO: call it from cplane
     276            0 :         (&Method::GET, "/installed_extensions") => {
     277            0 :             info!("serving /installed_extensions GET request");
     278            0 :             let status = compute.get_status();
     279            0 :             if status != ComputeStatus::Running {
     280            0 :                 let msg = format!(
     281            0 :                     "invalid compute status for extensions request: {:?}",
     282            0 :                     status
     283            0 :                 );
     284            0 :                 error!(msg);
     285            0 :                 return Response::new(Body::from(msg));
     286            0 :             }
     287            0 : 
     288            0 :             let connstr = compute.connstr.clone();
     289            0 :             let res = crate::installed_extensions::get_installed_extensions(connstr).await;
     290            0 :             match res {
     291            0 :                 Ok(res) => render_json(Body::from(serde_json::to_string(&res).unwrap())),
     292            0 :                 Err(e) => render_json_error(
     293            0 :                     &format!("could not get list of installed extensions: {}", e),
     294            0 :                     StatusCode::INTERNAL_SERVER_ERROR,
     295            0 :                 ),
     296              :             }
     297              :         }
     298              : 
     299            0 :         (&Method::POST, "/failpoints") if cfg!(feature = "testing") => {
     300            0 :             match failpoints_handler(req, CancellationToken::new()).await {
     301            0 :                 Ok(r) => r,
     302            0 :                 Err(ApiError::BadRequest(e)) => {
     303            0 :                     render_json_error(&e.to_string(), StatusCode::BAD_REQUEST)
     304              :                 }
     305              :                 Err(_) => {
     306            0 :                     render_json_error("Internal server error", StatusCode::INTERNAL_SERVER_ERROR)
     307              :                 }
     308              :             }
     309              :         }
     310              : 
     311              :         // download extension files from remote extension storage on demand
     312            0 :         (&Method::POST, route) if route.starts_with("/extension_server/") => {
     313            0 :             info!("serving {:?} POST request", route);
     314            0 :             info!("req.uri {:?}", req.uri());
     315              : 
     316              :             // don't even try to download extensions
     317              :             // if no remote storage is configured
     318            0 :             if compute.ext_remote_storage.is_none() {
     319            0 :                 info!("no extensions remote storage configured");
     320            0 :                 let mut resp = Response::new(Body::from("no remote storage configured"));
     321            0 :                 *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
     322            0 :                 return resp;
     323            0 :             }
     324            0 : 
     325            0 :             let mut is_library = false;
     326            0 :             if let Some(params) = req.uri().query() {
     327            0 :                 info!("serving {:?} POST request with params: {}", route, params);
     328            0 :                 if params == "is_library=true" {
     329            0 :                     is_library = true;
     330            0 :                 } else {
     331            0 :                     let mut resp = Response::new(Body::from("Wrong request parameters"));
     332            0 :                     *resp.status_mut() = StatusCode::BAD_REQUEST;
     333            0 :                     return resp;
     334              :                 }
     335            0 :             }
     336            0 :             let filename = route.split('/').last().unwrap().to_string();
     337            0 :             info!("serving /extension_server POST request, filename: {filename:?} is_library: {is_library}");
     338              : 
     339              :             // get ext_name and path from spec
     340              :             // don't lock compute_state for too long
     341            0 :             let ext = {
     342            0 :                 let compute_state = compute.state.lock().unwrap();
     343            0 :                 let pspec = compute_state.pspec.as_ref().expect("spec must be set");
     344            0 :                 let spec = &pspec.spec;
     345            0 : 
     346            0 :                 // debug only
     347            0 :                 info!("spec: {:?}", spec);
     348              : 
     349            0 :                 let remote_extensions = match spec.remote_extensions.as_ref() {
     350            0 :                     Some(r) => r,
     351              :                     None => {
     352            0 :                         info!("no remote extensions spec was provided");
     353            0 :                         let mut resp = Response::new(Body::from("no remote storage configured"));
     354            0 :                         *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
     355            0 :                         return resp;
     356              :                     }
     357              :                 };
     358              : 
     359            0 :                 remote_extensions.get_ext(
     360            0 :                     &filename,
     361            0 :                     is_library,
     362            0 :                     &compute.build_tag,
     363            0 :                     &compute.pgversion,
     364            0 :                 )
     365            0 :             };
     366            0 : 
     367            0 :             match ext {
     368            0 :                 Ok((ext_name, ext_path)) => {
     369            0 :                     match compute.download_extension(ext_name, ext_path).await {
     370            0 :                         Ok(_) => Response::new(Body::from("OK")),
     371            0 :                         Err(e) => {
     372            0 :                             error!("extension download failed: {}", e);
     373            0 :                             let mut resp = Response::new(Body::from(e.to_string()));
     374            0 :                             *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
     375            0 :                             resp
     376              :                         }
     377              :                     }
     378              :                 }
     379            0 :                 Err(e) => {
     380            0 :                     warn!("extension download failed to find extension: {}", e);
     381            0 :                     let mut resp = Response::new(Body::from("failed to find file"));
     382            0 :                     *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
     383            0 :                     resp
     384              :                 }
     385              :             }
     386              :         }
     387              : 
     388              :         // Return the `404 Not Found` for any other routes.
     389              :         _ => {
     390            0 :             let mut not_found = Response::new(Body::from("404 Not Found"));
     391            0 :             *not_found.status_mut() = StatusCode::NOT_FOUND;
     392            0 :             not_found
     393              :         }
     394              :     }
     395            0 : }
     396              : 
     397            0 : async fn handle_configure_request(
     398            0 :     req: Request<Body>,
     399            0 :     compute: &Arc<ComputeNode>,
     400            0 : ) -> Result<String, (String, StatusCode)> {
     401            0 :     if !compute.live_config_allowed {
     402            0 :         return Err((
     403            0 :             "live configuration is not allowed for this compute node".to_string(),
     404            0 :             StatusCode::PRECONDITION_FAILED,
     405            0 :         ));
     406            0 :     }
     407              : 
     408            0 :     let body_bytes = hyper::body::to_bytes(req.into_body()).await.unwrap();
     409            0 :     let spec_raw = String::from_utf8(body_bytes.to_vec()).unwrap();
     410            0 :     if let Ok(request) = serde_json::from_str::<ConfigurationRequest>(&spec_raw) {
     411            0 :         let spec = request.spec;
     412              : 
     413            0 :         let parsed_spec = match ParsedSpec::try_from(spec) {
     414            0 :             Ok(ps) => ps,
     415            0 :             Err(msg) => return Err((msg, StatusCode::BAD_REQUEST)),
     416              :         };
     417              : 
     418              :         // XXX: wrap state update under lock in code blocks. Otherwise,
     419              :         // we will try to `Send` `mut state` into the spawned thread
     420              :         // bellow, which will cause error:
     421              :         // ```
     422              :         // error: future cannot be sent between threads safely
     423              :         // ```
     424              :         {
     425            0 :             let mut state = compute.state.lock().unwrap();
     426            0 :             if state.status != ComputeStatus::Empty && state.status != ComputeStatus::Running {
     427            0 :                 let msg = format!(
     428            0 :                     "invalid compute status for configuration request: {:?}",
     429            0 :                     state.status.clone()
     430            0 :                 );
     431            0 :                 return Err((msg, StatusCode::PRECONDITION_FAILED));
     432            0 :             }
     433            0 :             state.pspec = Some(parsed_spec);
     434            0 :             state.set_status(ComputeStatus::ConfigurationPending, &compute.state_changed);
     435            0 :             drop(state);
     436            0 :             info!("set new spec and notified waiters");
     437              :         }
     438              : 
     439              :         // Spawn a blocking thread to wait for compute to become Running.
     440              :         // This is needed to do not block the main pool of workers and
     441              :         // be able to serve other requests while some particular request
     442              :         // is waiting for compute to finish configuration.
     443            0 :         let c = compute.clone();
     444            0 :         task::spawn_blocking(move || {
     445            0 :             let mut state = c.state.lock().unwrap();
     446            0 :             while state.status != ComputeStatus::Running {
     447            0 :                 state = c.state_changed.wait(state).unwrap();
     448            0 :                 info!(
     449            0 :                     "waiting for compute to become Running, current status: {:?}",
     450            0 :                     state.status
     451              :                 );
     452              : 
     453            0 :                 if state.status == ComputeStatus::Failed {
     454            0 :                     let err = state.error.as_ref().map_or("unknown error", |x| x);
     455            0 :                     let msg = format!("compute configuration failed: {:?}", err);
     456            0 :                     return Err((msg, StatusCode::INTERNAL_SERVER_ERROR));
     457            0 :                 }
     458              :             }
     459              : 
     460            0 :             Ok(())
     461            0 :         })
     462            0 :         .await
     463            0 :         .unwrap()?;
     464              : 
     465              :         // Return current compute state if everything went well.
     466            0 :         let state = compute.state.lock().unwrap().clone();
     467            0 :         let status_response = status_response_from_state(&state);
     468            0 :         Ok(serde_json::to_string(&status_response).unwrap())
     469              :     } else {
     470            0 :         Err(("invalid spec".to_string(), StatusCode::BAD_REQUEST))
     471              :     }
     472            0 : }
     473              : 
     474            0 : fn render_json_error(e: &str, status: StatusCode) -> Response<Body> {
     475            0 :     let error = GenericAPIError {
     476            0 :         error: e.to_string(),
     477            0 :     };
     478            0 :     Response::builder()
     479            0 :         .status(status)
     480            0 :         .header(CONTENT_TYPE, "application/json")
     481            0 :         .body(Body::from(serde_json::to_string(&error).unwrap()))
     482            0 :         .unwrap()
     483            0 : }
     484              : 
     485            0 : fn render_json(body: Body) -> Response<Body> {
     486            0 :     Response::builder()
     487            0 :         .header(CONTENT_TYPE, "application/json")
     488            0 :         .body(body)
     489            0 :         .unwrap()
     490            0 : }
     491              : 
     492            0 : fn render_plain(body: Body) -> Response<Body> {
     493            0 :     Response::builder()
     494            0 :         .header(CONTENT_TYPE, "text/plain")
     495            0 :         .body(body)
     496            0 :         .unwrap()
     497            0 : }
     498              : 
     499            0 : async fn handle_terminate_request(compute: &Arc<ComputeNode>) -> Result<(), (String, StatusCode)> {
     500            0 :     {
     501            0 :         let mut state = compute.state.lock().unwrap();
     502            0 :         if state.status == ComputeStatus::Terminated {
     503            0 :             return Ok(());
     504            0 :         }
     505            0 :         if state.status != ComputeStatus::Empty && state.status != ComputeStatus::Running {
     506            0 :             let msg = format!(
     507            0 :                 "invalid compute status for termination request: {}",
     508            0 :                 state.status
     509            0 :             );
     510            0 :             return Err((msg, StatusCode::PRECONDITION_FAILED));
     511            0 :         }
     512            0 :         state.set_status(ComputeStatus::TerminationPending, &compute.state_changed);
     513            0 :         drop(state);
     514            0 :     }
     515            0 : 
     516            0 :     forward_termination_signal();
     517            0 :     info!("sent signal and notified waiters");
     518              : 
     519              :     // Spawn a blocking thread to wait for compute to become Terminated.
     520              :     // This is needed to do not block the main pool of workers and
     521              :     // be able to serve other requests while some particular request
     522              :     // is waiting for compute to finish configuration.
     523            0 :     let c = compute.clone();
     524            0 :     task::spawn_blocking(move || {
     525            0 :         let mut state = c.state.lock().unwrap();
     526            0 :         while state.status != ComputeStatus::Terminated {
     527            0 :             state = c.state_changed.wait(state).unwrap();
     528            0 :             info!(
     529            0 :                 "waiting for compute to become {}, current status: {:?}",
     530            0 :                 ComputeStatus::Terminated,
     531            0 :                 state.status
     532              :             );
     533              :         }
     534              : 
     535            0 :         Ok(())
     536            0 :     })
     537            0 :     .await
     538            0 :     .unwrap()?;
     539            0 :     info!("terminated Postgres");
     540            0 :     Ok(())
     541            0 : }
     542              : 
     543              : // Main Hyper HTTP server function that runs it and blocks waiting on it forever.
     544              : #[tokio::main]
     545            0 : async fn serve(port: u16, state: Arc<ComputeNode>) {
     546            0 :     // this usually binds to both IPv4 and IPv6 on linux
     547            0 :     // see e.g. https://github.com/rust-lang/rust/pull/34440
     548            0 :     let addr = SocketAddr::new(IpAddr::from(Ipv6Addr::UNSPECIFIED), port);
     549            0 : 
     550            0 :     let make_service = make_service_fn(move |_conn| {
     551            0 :         let state = state.clone();
     552            0 :         async move {
     553            0 :             Ok::<_, Infallible>(service_fn(move |req: Request<Body>| {
     554            0 :                 let state = state.clone();
     555            0 :                 async move {
     556            0 :                     Ok::<_, Infallible>(
     557            0 :                         // NOTE: We include the URI path in the string. It
     558            0 :                         // doesn't contain any variable parts or sensitive
     559            0 :                         // information in this API.
     560            0 :                         tracing_utils::http::tracing_handler(
     561            0 :                             req,
     562            0 :                             |req| routes(req, &state),
     563            0 :                             OtelName::UriPath,
     564            0 :                         )
     565            0 :                         .await,
     566            0 :                     )
     567            0 :                 }
     568            0 :             }))
     569            0 :         }
     570            0 :     });
     571            0 : 
     572            0 :     info!("starting HTTP server on {}", addr);
     573            0 : 
     574            0 :     let server = Server::bind(&addr).serve(make_service);
     575            0 : 
     576            0 :     // Run this server forever
     577            0 :     if let Err(e) = server.await {
     578            0 :         error!("server error: {}", e);
     579            0 :     }
     580            0 : }
     581              : 
     582              : /// Launch a separate Hyper HTTP API server thread and return its `JoinHandle`.
     583            0 : pub fn launch_http_server(port: u16, state: &Arc<ComputeNode>) -> Result<thread::JoinHandle<()>> {
     584            0 :     let state = Arc::clone(state);
     585            0 : 
     586            0 :     Ok(thread::Builder::new()
     587            0 :         .name("http-endpoint".into())
     588            0 :         .spawn(move || serve(port, state))?)
     589            0 : }
        

Generated by: LCOV version 2.1-beta