-
Notifications
You must be signed in to change notification settings - Fork 359
/
Copy pathmain.rs
109 lines (90 loc) · 3.37 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use aws_config::BehaviorVersion;
use aws_credential_types::provider::ProvideCredentials;
use aws_sigv4::{
http_request::{sign, SignableBody, SignableRequest, SigningSettings},
sign::v4,
};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use aws_lambda_json_impl::{json, Value};
use sqlx::postgres::PgConnectOptions;
use std::env;
use std::time::{Duration, SystemTime};
const RDS_CERTS: &[u8] = include_bytes!("global-bundle.pem");
async fn generate_rds_iam_token(
db_hostname: &str,
port: u16,
db_username: &str,
) -> Result<String, Error> {
let config = aws_config::load_defaults(BehaviorVersion::v2024_03_28()).await;
let credentials = config
.credentials_provider()
.expect("no credentials provider found")
.provide_credentials()
.await
.expect("unable to load credentials");
let identity = credentials.into();
let region = config.region().unwrap().to_string();
let mut signing_settings = SigningSettings::default();
signing_settings.expires_in = Some(Duration::from_secs(900));
signing_settings.signature_location = aws_sigv4::http_request::SignatureLocation::QueryParams;
let signing_params = v4::SigningParams::builder()
.identity(&identity)
.region(®ion)
.name("rds-db")
.time(SystemTime::now())
.settings(signing_settings)
.build()?;
let url = format!(
"https://{db_hostname}:{port}/?Action=connect&DBUser={db_user}",
db_hostname = db_hostname,
port = port,
db_user = db_username
);
let signable_request =
SignableRequest::new("GET", &url, std::iter::empty(), SignableBody::Bytes(&[]))
.expect("signable request");
let (signing_instructions, _signature) =
sign(signable_request, &signing_params.into())?.into_parts();
let mut url = url::Url::parse(&url).unwrap();
for (name, value) in signing_instructions.params() {
url.query_pairs_mut().append_pair(name, &value);
}
let response = url.to_string().split_off("https://".len());
Ok(response)
}
#[tokio::main]
async fn main() -> Result<(), Error> {
run(service_fn(handler)).await
}
async fn handler(_event: LambdaEvent<Value>) -> Result<Value, Error> {
let db_host = env::var("DB_HOSTNAME").expect("DB_HOSTNAME must be set");
let db_port = env::var("DB_PORT")
.expect("DB_PORT must be set")
.parse::<u16>()
.expect("PORT must be a valid number");
let db_name = env::var("DB_NAME").expect("DB_NAME must be set");
let db_user_name = env::var("DB_USERNAME").expect("DB_USERNAME must be set");
let token = generate_rds_iam_token(&db_host, db_port, &db_user_name).await?;
let opts = PgConnectOptions::new()
.host(&db_host)
.port(db_port)
.username(&db_user_name)
.password(&token)
.database(&db_name)
.ssl_root_cert_from_pem(RDS_CERTS.to_vec())
.ssl_mode(sqlx::postgres::PgSslMode::Require);
let pool = sqlx::postgres::PgPoolOptions::new()
.connect_with(opts)
.await?;
let result: i32 = sqlx::query_scalar("SELECT $1 + $2")
.bind(3)
.bind(2)
.fetch_one(&pool)
.await?;
println!("Result: {:?}", result);
Ok(json!({
"statusCode": 200,
"content-type": "text/plain",
"body": format!("The selected sum is: {result}")
}))
}