Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(serverless,runtime): various fixes #164

Merged
merged 11 commits into from
Oct 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/giant-squids-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lagon/serverless': patch
---

Fix URL format
5 changes: 5 additions & 0 deletions .changeset/old-eyes-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lagon/serverless': patch
---

Fix domains assignement
5 changes: 5 additions & 0 deletions .changeset/serious-cobras-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lagon/runtime': patch
---

Update rusty_v8 to 0.51
5 changes: 5 additions & 0 deletions .changeset/sweet-kids-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lagon/serverless': patch
---

Use TLS for Redis / MySQL connections
5 changes: 5 additions & 0 deletions .changeset/thick-brooms-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lagon/runtime': patch
---

Load ICU data to enable i18n
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion docker/serverless.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM rust:1.63 as builder

WORKDIR /app
COPY . .
COPY . packages/serverless/.env ./

WORKDIR /app/packages/serverless
RUN cargo build --release
Expand All @@ -10,6 +10,7 @@ RUN cargo build --release
FROM rust:1.63

COPY --from=builder /app/target/release/lagon-serverless /usr/local/bin/lagon-serverless
COPY --from=builder /app/.env ./.env

EXPOSE 4000
CMD ["lagon-serverless"]
2 changes: 1 addition & 1 deletion packages/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ version = "0.1.0"
edition = "2021"

[dependencies]
v8 = "0.45.0"
v8 = "0.51.0"
tokio = { version = "1", features = ["full"] }
hyper = { version = "0.14", features = ["client", "http1", "http2", "tcp"] }
Binary file added packages/runtime/icudtl.dat
Binary file not shown.
20 changes: 11 additions & 9 deletions packages/runtime/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use v8::V8;

use crate::isolate::IsolateOptions;

#[repr(C, align(16))]
struct IcuData([u8; 10454784]);

static JS_RUNTIME: &str = include_str!("../../runtime.js");
static ICU_DATA: IcuData = IcuData(*include_bytes!("../../icudtl.dat"));

pub struct RuntimeOptions {
allow_eval: bool,
Expand All @@ -21,15 +25,19 @@ unsafe impl Sync for Runtime {}

impl Runtime {
pub fn new(options: RuntimeOptions) -> Self {
let platform = v8::new_default_platform(0, false).make_shared();
V8::initialize_platform(platform);
V8::initialize();
// Load ICU data to enable i18n, similar to Deno:
// https://github.com/denoland/deno/blob/a55b194638bcaace38917703b7d9233fb1989d44/core/runtime.rs#L223
v8::icu::set_common_data_71(&ICU_DATA.0).unwrap();

// Disable code generation from `eval(...)` / `new Function(...)`
if !options.allow_eval {
V8::set_flags_from_string("--disallow-code-generation-from-strings");
}

let platform = v8::new_default_platform(0, false).make_shared();
V8::initialize_platform(platform);
V8::initialize();

Runtime {}
}

Expand All @@ -42,12 +50,6 @@ impl Runtime {
}
}

impl Drop for Runtime {
fn drop(&mut self) {
self.dispose();
}
}

pub fn get_runtime_code<'a>(
scope: &mut v8::HandleScope<'a, ()>,
options: &IsolateOptions,
Expand Down
7 changes: 2 additions & 5 deletions packages/runtime/tests/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@ use std::{collections::HashMap, sync::Once};
use lagon_runtime::{
http::{Method, Request, Response, RunResult},
isolate::{Isolate, IsolateOptions},
runtime::{Runtime, RuntimeOptions},
};

fn setup() {
static START: Once = Once::new();

START.call_once(|| {
let platform = v8::new_default_platform(0, false).make_shared();
v8::V8::initialize_platform(platform);
v8::V8::initialize();

v8::V8::set_flags_from_string("--disallow-code-generation-from-strings");
Runtime::new(RuntimeOptions::default());
});
}

Expand Down
1 change: 0 additions & 1 deletion packages/serverless/.env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
LAGON_TOKEN=
LAGON_ROOT_DOMAIN=lagon.app
LAGON_WORKERS=1
LAGON_REGION=
SENTRY_DSN=
SENTRY_ENVIRONMENT=development
Expand Down
2 changes: 1 addition & 1 deletion packages/serverless/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ flume = "0.10.14"
mysql = "22.2.0"
dotenv = "0.15.0"
rust-s3 = "0.32"
redis = "0.21.6"
redis = { version = "0.21.6", features = ["tls"] }
serde_json = "1.0"
metrics = "0.20.1"
metrics-exporter-prometheus = { version = "0.11.0", features = ["http-listener"] }
Expand Down
30 changes: 28 additions & 2 deletions packages/serverless/src/deployments/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,36 @@ pub mod pubsub;
pub struct Deployment {
pub id: String,
pub function_id: String,
pub function_name: String,
pub domains: HashSet<String>,
pub assets: HashSet<String>,
pub environment_variables: HashMap<String, String>,
pub memory: usize, // in MB (MegaBytes)
pub timeout: usize, // in ms (MilliSeconds)
}

impl Deployment {
pub fn get_domains(&self) -> Vec<String> {
let mut domains = Vec::new();

domains.push(format!(
"{}.{}",
self.id,
dotenv::var("LAGON_ROOT_DOMAIN").expect("LAGON_ROOT_DOMAIN must be set")
));

// TODO: should only set function name and domains deployments when deployment is the production one
domains.push(format!(
"{}.{}",
self.function_name,
dotenv::var("LAGON_ROOT_DOMAIN").expect("LAGON_ROOT_DOMAIN must be set")
));
domains.extend(self.domains.clone());

domains
}
}

pub async fn get_deployments(
mut conn: PooledConn,
bucket: Bucket,
Expand All @@ -44,6 +67,7 @@ pub async fn get_deployments(
SELECT
Deployment.id,
Function.id,
Function.name,
Function.memory,
Function.timeout,
Domain.domain,
Expand All @@ -57,7 +81,8 @@ pub async fn get_deployments(
LEFT JOIN Asset
ON Deployment.id = Asset.deploymentId
",
|(id, function_id, memory, timeout, domain, asset): (
|(id, function_id, function_name, memory, timeout, domain, asset): (
String,
String,
String,
usize,
Expand All @@ -79,6 +104,7 @@ pub async fn get_deployments(
.or_insert(Deployment {
id,
function_id,
function_name,
domains: domain
.map(|domain| {
let mut domains = HashSet::new();
Expand Down Expand Up @@ -126,7 +152,7 @@ pub async fn get_deployments(
}
}

for domain in deployment.domains.clone() {
for domain in deployment.get_domains() {
deployments.insert(domain, deployment.clone());
}
}
Expand Down
5 changes: 3 additions & 2 deletions packages/serverless/src/deployments/pubsub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub fn listen_pub_sub(
let deployment = Deployment {
id: value["deploymentId"].as_str().unwrap().to_string(),
function_id: value["functionId"].as_str().unwrap().to_string(),
function_name: value["functionName"].as_str().unwrap().to_string(),
assets: value["assets"]
.as_array()
.unwrap()
Expand Down Expand Up @@ -59,7 +60,7 @@ pub fn listen_pub_sub(
Ok(_) => {
let mut deployments = deployments.write().await;

for domain in deployment.domains.clone() {
for domain in deployment.get_domains() {
deployments.insert(domain, deployment.clone());
}
}
Expand All @@ -76,7 +77,7 @@ pub fn listen_pub_sub(
Ok(_) => {
let mut deployments = deployments.write().await;

for domain in deployment.domains.clone() {
for domain in deployment.get_domains() {
deployments.remove(&domain);
}
}
Expand Down
6 changes: 5 additions & 1 deletion packages/serverless/src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ pub async fn hyper_request_to_request(request: HyperRequest<Body>) -> Request {
_ => Method::GET,
};

let url = request.uri().to_string();
let host = headers
.get("host")
.map(|host| host.to_string())
.unwrap_or(String::new());
let url = format!("http://{}{}", host, request.uri().to_string().as_str());

let body = body::to_bytes(request.into_body()).await.unwrap();
let body = String::from_utf8(body.to_vec()).unwrap();
Expand Down
21 changes: 14 additions & 7 deletions packages/serverless/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use lagon_runtime::isolate::{Isolate, IsolateOptions};
use lagon_runtime::runtime::{Runtime, RuntimeOptions};
use metrics::{counter, histogram, increment_counter};
use metrics_exporter_prometheus::PrometheusBuilder;
use mysql::Pool;
use mysql::{Opts, OptsBuilder, Pool, SslOpts};
use s3::creds::Credentials;
use s3::Bucket;
use std::collections::HashMap;
Expand Down Expand Up @@ -62,7 +62,7 @@ async fn main() {
init_logger().expect("Failed to init logger");

let runtime = Runtime::new(RuntimeOptions::default());
let addr = SocketAddr::from(([127, 0, 0, 1], 4000));
let addr = SocketAddr::from(([0, 0, 0, 0], 4000));

let (request_tx, request_rx) = flume::unbounded::<HyperRequest<Body>>();
let (response_tx, response_rx) = flume::unbounded::<RunResult>();
Expand All @@ -83,7 +83,12 @@ async fn main() {

let url = dotenv::var("DATABASE_URL").expect("DATABASE_URL must be set");
let url = url.as_str();
let pool = Pool::new(url).unwrap();
let opts = Opts::from_url(url).expect("Failed to parse DATABASE_URL");
#[cfg(not(debug_assertions))]
let opts = OptsBuilder::from_opts(opts).ssl_opts(Some(
SslOpts::default().with_danger_accept_invalid_certs(true),
));
let pool = Pool::new(opts).unwrap();
let conn = pool.get_conn().unwrap();

let bucket_name = dotenv::var("S3_BUCKET").expect("S3_BUCKET must be set");
Expand Down Expand Up @@ -111,16 +116,18 @@ async fn main() {

loop {
let hyper_request = request_rx.recv_async().await.unwrap();

let mut url = hyper_request.uri().to_string();
// Remove the leading '/' from the url
url.remove(0);

let request = hyper_request_to_request(hyper_request).await;

let hostname = request.headers.get("host").unwrap().clone();
let deployments = deployments.read().await;

match deployments.get(&hostname) {
Some(deployment) => {
let url = &mut request.url.clone();
url.remove(0);

let labels = vec![
("deployment", deployment.id.clone()),
("function", deployment.function_id.clone()),
Expand All @@ -129,7 +136,7 @@ async fn main() {
increment_counter!("lagon_requests", &labels);
counter!("lagon_bytes_in", request.len() as u64, &labels);

if let Some(asset) = deployment.assets.iter().find(|asset| *asset == url) {
if let Some(asset) = deployment.assets.iter().find(|asset| *asset == &url) {
// TODO: handle read error
let response = handle_asset(deployment, asset).unwrap();
let response = RunResult::Response(response);
Expand Down
2 changes: 1 addition & 1 deletion packages/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"formidable": "^2.0.1",
"monaco-editor": "^0.33.0",
"next": "12.1.6",
"next-auth": "^4.3.4",
"next-auth": "^4.12.2",
"next-international": "^0.2.0",
"node-fetch": "^3.2.10",
"react": "18.1.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/website/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma),
providers: [
GithubProvider({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
clientId: process.env.GITHUB_CLIENT_ID || '',
clientSecret: process.env.GITHUB_CLIENT_SECRET || '',
}),
],
secret: process.env.NEXTAUTH_SECRET,
Expand Down
Loading