diff --git a/compute_tools/src/compute.rs b/compute_tools/src/compute.rs index 285be56264..329cc78573 100644 --- a/compute_tools/src/compute.rs +++ b/compute_tools/src/compute.rs @@ -19,6 +19,7 @@ use futures::future::join_all; use futures::stream::FuturesUnordered; use futures::StreamExt; use nix::unistd::Pid; +use postgres::config::Config; use postgres::error::SqlState; use postgres::{Client, NoTls}; use tracing::{debug, error, info, instrument, warn}; @@ -1367,6 +1368,28 @@ LIMIT 100", download_size } + pub fn set_role_grants( + &self, + db_name: &str, + schema_name: &str, + privilege: &str, + role_name: &str, + ) -> Result<()> { + let mut conf = Config::from_str(self.connstr.as_str()).unwrap(); + conf.dbname(db_name); + + let mut db_client = conf + .connect(NoTls) + .context("Failed to connect to the database")?; + + let query = "GRANT $1 ON SCHEMA $2 TO $3"; + db_client + .execute(query, &[&schema_name, &privilege, &role_name]) + .context(format!("Failed to execute query: {}", query))?; + + Ok(()) + } + #[tokio::main] pub async fn prepare_preload_libraries( &self, diff --git a/compute_tools/src/http/api.rs b/compute_tools/src/http/api.rs index 79e6158081..60d542af4c 100644 --- a/compute_tools/src/http/api.rs +++ b/compute_tools/src/http/api.rs @@ -9,8 +9,10 @@ use crate::catalog::SchemaDumpError; use crate::catalog::{get_database_schema, get_dbs_and_roles}; use crate::compute::forward_termination_signal; use crate::compute::{ComputeNode, ComputeState, ParsedSpec}; -use compute_api::requests::ConfigurationRequest; -use compute_api::responses::{ComputeStatus, ComputeStatusResponse, GenericAPIError}; +use compute_api::requests::{ConfigurationRequest, SetRoleGrantsRequest}; +use compute_api::responses::{ + ComputeStatus, ComputeStatusResponse, GenericAPIError, SetRoleGrantsResult, +}; use anyhow::Result; use hyper::header::CONTENT_TYPE; @@ -165,6 +167,35 @@ async fn routes(req: Request, compute: &Arc) -> Response { + info!("serving /grants POST request"); + let status = compute.get_status(); + if status != ComputeStatus::Running { + let msg = format!( + "invalid compute status for set_role_grants request: {:?}", + status + ); + error!(msg); + return Response::new(Body::from(msg)); + } + + let request = hyper::body::to_bytes(req.into_body()).await.unwrap(); + let request = serde_json::from_slice::(&request).unwrap(); + let res = compute.set_role_grants( + &request.database, + &request.schema, + &request.privilege, + &request.role, + ); + match res { + Ok(_) => Response::new(Body::from("true")), + Err(e) => { + error!("set_role_grants failed: {}", e); + Response::new(Body::from(e.to_string())) + } + } + } + // get the list of installed extensions // currently only used in python tests // TODO: call it from cplane diff --git a/compute_tools/src/http/openapi_spec.yaml b/compute_tools/src/http/openapi_spec.yaml index e9fa66b323..6b54a9e986 100644 --- a/compute_tools/src/http/openapi_spec.yaml +++ b/compute_tools/src/http/openapi_spec.yaml @@ -10,7 +10,7 @@ paths: /status: get: tags: - - Info + - Info summary: Get compute node internal status. description: "" operationId: getComputeStatus @@ -25,7 +25,7 @@ paths: /metrics.json: get: tags: - - Info + - Info summary: Get compute node startup metrics in JSON format. description: "" operationId: getComputeMetricsJSON @@ -40,7 +40,7 @@ paths: /insights: get: tags: - - Info + - Info summary: Get current compute insights in JSON format. description: | Note, that this doesn't include any historical data. @@ -56,7 +56,7 @@ paths: /installed_extensions: get: tags: - - Info + - Info summary: Get installed extensions. description: "" operationId: getInstalledExtensions @@ -70,7 +70,7 @@ paths: /info: get: tags: - - Info + - Info summary: Get info about the compute pod / VM. description: "" operationId: getInfo @@ -130,7 +130,7 @@ paths: /check_writability: post: tags: - - Check + - Check summary: Check that we can write new data on this compute. description: "" operationId: checkComputeWritability @@ -147,7 +147,7 @@ paths: /configure: post: tags: - - Configure + - Configure summary: Perform compute node configuration. description: | This is a blocking API endpoint, i.e. it blocks waiting until @@ -201,7 +201,7 @@ paths: /extension_server: post: tags: - - Extension + - Extension summary: Download extension from S3 to local folder. description: "" operationId: downloadExtension @@ -230,7 +230,7 @@ paths: /terminate: post: tags: - - Terminate + - Terminate summary: Terminate Postgres and wait for it to exit description: "" operationId: terminate @@ -369,7 +369,7 @@ components: moment, when spec was received. example: "2022-10-12T07:20:50.52Z" status: - $ref: '#/components/schemas/ComputeStatus' + $ref: "#/components/schemas/ComputeStatus" last_active: type: string description: | @@ -427,6 +427,28 @@ components: n_databases: type: integer + SetRoleGrantsRequest: + type: object + required: + - database + - role + - grants + properties: + database: + type: string + description: Database name. + example: "neondb" + role: + type: string + description: Role name. + example: "neon" + grants: + type: array + items: + type: string + description: List of grants to set. + example: ["SELECT", "INSERT"] + # # Errors # diff --git a/libs/compute_api/src/requests.rs b/libs/compute_api/src/requests.rs index 5896c7dc65..963c5992af 100644 --- a/libs/compute_api/src/requests.rs +++ b/libs/compute_api/src/requests.rs @@ -12,3 +12,11 @@ use serde::Deserialize; pub struct ConfigurationRequest { pub spec: ComputeSpec, } + +#[derive(Deserialize, Debug)] +pub struct SetRoleGrantsRequest { + pub database: String, + pub schema: String, + pub privilege: String, + pub role: String, +} diff --git a/libs/compute_api/src/responses.rs b/libs/compute_api/src/responses.rs index 5023fce003..4b4b16ebc4 100644 --- a/libs/compute_api/src/responses.rs +++ b/libs/compute_api/src/responses.rs @@ -168,3 +168,8 @@ pub struct InstalledExtension { pub struct InstalledExtensions { pub extensions: Vec, } + +#[derive(Clone, Debug, Default, Serialize)] +pub struct SetRoleGrantsResult { + pub extension: String, +}