Skip to content

Commit

Permalink
Hello, world!
Browse files Browse the repository at this point in the history
  • Loading branch information
defcronyke committed Jun 3, 2021
1 parent ca98463 commit da26267
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 40 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
Cargo.lock
credentials.env
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ path = "src/example1/main.rs"

[dependencies]
# Run this once first: cargo install cargo-watch
tokio = { version = "0.2", features = ["macros", "rt-threaded"] }
warp = "0.2"
tokio = { version = "1", features = ["full"] }
warp = "0.3"
warp-real-ip = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
42 changes: 22 additions & 20 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
# Borrowed and modified example from:
# https://blog.logrocket.com/packaging-a-rust-web-service-using-docker/

# --- build image ---
FROM rust:1.43 as build

RUN USER=root cargo new --bin rust-playground-api
FROM rust_playground_api_deps:latest as build

WORKDIR ./rust-playground-api
ARG APP=/usr/src/app

COPY ./Cargo.toml ./Cargo.toml
ENV TZ=Etc/UTC \
APP_USER=appuser

ADD . ./
ADD ./src ./src
WORKDIR ./rust-playground-api

RUN cargo build --release

RUN rm src/*.rs && \
rm ./target/release/deps/main* && \
rm -rf target/debug && \
rm .gitignore && \
rm Dockerfile && \
rm LICENSE && \
rm serve.sh && \
rm start.sh && \
rm stop.sh && \
rm deps.sh
RUN rm src/*.rs || true; \
rm ./target/release/deps/main* || true; \
rm -rf target/debug || true; \
rm -rf deps || true; \
rm .gitignore || true; \
rm Dockerfile || true; \
rm LICENSE || true; \
rm serve.sh || true; \
rm start.sh || true; \
rm stop.sh || true; \
rm deps.sh || true


# --- run image ---
Expand All @@ -46,6 +42,12 @@ RUN groupadd $APP_USER && \
mkdir -p ${APP}

COPY --from=build /rust-playground-api/target/release/main ${APP}/main
COPY --from=build /rust-playground-api/asm.sh ${APP}/
COPY --from=build /rust-playground-api/build.sh ${APP}/
COPY --from=build /rust-playground-api/run.sh ${APP}/
COPY --from=build /rust-playground-api/test.sh ${APP}/
COPY --from=build /rust-playground-api/wasm.sh ${APP}/
COPY --from=build /rust-playground-api/src/example1/main.rs ${APP}/src/example1/main.rs

RUN chown -R $APP_USER:$APP_USER ${APP}

Expand Down
9 changes: 5 additions & 4 deletions deps.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#!/bin/bash
# Build the Docker image for the container.
# Build the Docker dependencies image.
#
# NOTE: You have to run this script once first,
# and afterwards you can run the following
# script to start the container: ./start.sh
# and afterwards you have to run the following
# script to build the production Docker
# image: ./prod.sh

rust_playground_api_deps() {
input_str="$@"

docker build -t rust_playground_api:latest .
docker build -t rust_playground_api_deps:latest -f deps/Dockerfile .
}

rust_playground_api_deps "$@"
13 changes: 13 additions & 0 deletions deps/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# --- dependencies image ---
FROM rust:latest

RUN USER=root cargo new --bin rust-playground-api

WORKDIR ./rust-playground-api

COPY ./Cargo.toml ./Cargo.toml

ADD . ./
ADD ./src ./src

RUN cargo build --release
14 changes: 14 additions & 0 deletions prod.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
# Build the Docker image for the container.
#
# NOTE: You have to run this script once first,
# and afterwards you can run the following
# script to start the container: ./start.sh

rust_playground_api_prod() {
input_str="$@"

docker build -t gcr.io/rust-playground-api/rust_playground_api:latest .
}

rust_playground_api_prod "$@"
3 changes: 3 additions & 0 deletions push.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

docker push gcr.io/rust-playground-api/rust_playground_api:latest
243 changes: 231 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,240 @@
/* Borrowed and modified example from:
https://blog.logrocket.com/packaging-a-rust-web-service-using-docker/
*/

use warp::{Filter, Rejection, Reply};
use serde::Serialize;
use std::env;
use std::error::Error;
use std::net::IpAddr;
use std::process::Command;
use warp::http::header::{HeaderMap, HeaderValue};
use warp::http::StatusCode;
use warp::reject::Rejection;
use warp::{reject, Filter, Reply};
use warp_real_ip::real_ip;

type Result<T> = std::result::Result<T, Rejection>;

#[derive(Debug, Serialize)]
struct ErrorMessage {
code: u16,
message: String,
}

impl reject::Reject for ErrorMessage {}

#[derive(Debug)]
struct DivideByZero;

impl reject::Reject for DivideByZero {}

async fn health_handler(addr: String) -> Result<impl Reply> {
println!("|info| GET /health | from: {}", addr);

Ok("{\"code\": 200, \"message\": \"200 OK\"}")
}

async fn asm_handler(addr: String) -> Result<impl Reply> {
println!("|info| GET /asm | from: {}", addr);

let output = Command::new("/bin/bash")
.arg("-c")
.arg("./asm.sh")
.output()
.expect("failed to execute process");

let stdout = String::from_utf8_lossy(&output.stdout).to_string();

println!("status: {}", output.status);
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

Ok(stdout)
}

async fn build_handler(addr: String) -> Result<impl Reply> {
println!("|info| GET /build | from: {}", addr);

let output = Command::new("/bin/bash")
.arg("-c")
.arg("./build.sh")
.output()
.expect("failed to execute process");

let stdout = String::from_utf8_lossy(&output.stdout).to_string();

println!("status: {}", output.status);
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

Ok(stdout)
}

async fn run_handler(addr: String) -> Result<impl Reply> {
println!("|info| GET / (or GET /run) | from: {}", addr);

let output = Command::new("/bin/bash")
.arg("-c")
.arg("./run.sh")
.output()
.expect("failed to execute process");

let stdout = String::from_utf8_lossy(&output.stdout).to_string();

println!("status: {}", output.status);
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

Ok(stdout)
}

async fn test_handler(addr: String) -> Result<impl Reply> {
println!("|info| GET /test | from: {}", addr);

let output = Command::new("/bin/bash")
.arg("-c")
.arg("./test.sh")
.output()
.expect("failed to execute process");

let stdout = String::from_utf8_lossy(&output.stdout).to_string();

println!("status: {}", output.status);
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

Ok(stdout)
}

async fn wasm_handler(addr: String) -> Result<impl Reply> {
println!("|info| GET /wasm | from: {}", addr);

let output = Command::new("/bin/bash")
.arg("-c")
.arg("./wasm.sh")
.output()
.expect("failed to execute process");

let stdout = String::from_utf8_lossy(&output.stdout).to_string();

println!("status: {}", output.status);
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));

Ok(stdout)
}

async fn rejection_handler(err: Rejection) -> Result<impl Reply> {
let code;
let message;

if err.is_not_found() {
code = StatusCode::NOT_FOUND;
message = "404 Not Found";
} else if let Some(DivideByZero) = err.find() {
code = StatusCode::BAD_REQUEST;
message = "400 Bad Request";
} else if let Some(e) = err.find::<warp::filters::body::BodyDeserializeError>() {
message = match e.source() {
Some(cause) => {
if cause.to_string().contains("denom") {
"400 Bad Request"
} else {
"400 Bad Request"
}
}
None => "400 Bad Request",
};
code = StatusCode::BAD_REQUEST;
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
code = StatusCode::METHOD_NOT_ALLOWED;
message = "405 Method Not Allowed";
} else {
eprintln!("unhandled rejection: {:?}", err);
code = StatusCode::INTERNAL_SERVER_ERROR;
message = "500 Internal Server Error";
}

let json = warp::reply::json(&ErrorMessage {
code: code.as_u16(),
message: message.into(),
});

Ok(warp::reply::with_status(json, code))
}

#[tokio::main]
async fn main() {
let health_route = warp::path!("health").and_then(health_handler);
let port_default: u16 = 8080;

let routes = health_route.with(warp::cors().allow_any_origin());
let port: u16 = {
env::var("PORT")
.unwrap_or(format!("{}", port_default))
.parse()
.unwrap_or(port_default)
};

println!("Started server at: http://localhost:8080");
warp::serve(routes).run(([0, 0, 0, 0], 8080)).await;
}
let proxy_addr = [127, 0, 0, 1].into();

let mut res_headers = HeaderMap::new();
res_headers.insert(
"Content-Type",
HeaderValue::from_static("application/json; charset=utf-8"),
);

let health_route = warp::path!("health").and(
real_ip(vec![proxy_addr])
.map(|addr: Option<IpAddr>| format!("{:?}", addr.unwrap()))
.and_then(health_handler),
);

let asm_route = warp::path!("asm").and(
real_ip(vec![proxy_addr])
.map(|addr: Option<IpAddr>| format!("{:?}", addr.unwrap()))
.and_then(asm_handler),
);

let build_route = warp::path!("build").and(
real_ip(vec![proxy_addr])
.map(|addr: Option<IpAddr>| format!("{:?}", addr.unwrap()))
.and_then(build_handler),
);

let run_route = warp::path!("run").and(
real_ip(vec![proxy_addr])
.map(|addr: Option<IpAddr>| format!("{:?}", addr.unwrap()))
.and_then(run_handler),
);

let test_route = warp::path!("test").and(
real_ip(vec![proxy_addr])
.map(|addr: Option<IpAddr>| format!("{:?}", addr.unwrap()))
.and_then(test_handler),
);

let wasm_route = warp::path!("wasm").and(
real_ip(vec![proxy_addr])
.map(|addr: Option<IpAddr>| format!("{:?}", addr.unwrap()))
.and_then(wasm_handler),
);

let index_route = warp::path::end().and(
real_ip(vec![proxy_addr])
.map(|addr: Option<IpAddr>| format!("{:?}", addr.unwrap()))
.and_then(run_handler),
);

let routes = warp::any().and(
warp::get()
.and(
health_route
.or(asm_route)
.or(build_route)
.or(run_route)
.or(test_route)
.or(wasm_route)
.or(index_route)
.recover(rejection_handler)
.with(warp::reply::with::headers(res_headers.clone()))
.with(warp::cors().allow_any_origin()),
)
.recover(rejection_handler)
.with(warp::reply::with::headers(res_headers.clone()))
.with(warp::cors().allow_any_origin()),
);

println!("Started server at: http://localhost:{}", port);

async fn health_handler() -> Result<impl Reply> {
Ok("OK")
warp::serve(routes).run(([0, 0, 0, 0], port)).await;
}
4 changes: 2 additions & 2 deletions start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
# and the docker logs viewer will be attached to it.
#
# NOTE: Run this once first to build the
# Docker image: ./deps.sh
# Docker image: ./deps.sh && ./prod.sh
#
# NOTE: To stop the container, you can run
# the following script: ./stop.sh

rust_playground_api_start() {
input_str="$@"

docker run --rm -it -dp 8080:8080 --name rust_playground_api rust_playground_api:latest
docker run --rm -it -dp 8080:8080 --name rust_playground_api gcr.io/rust-playground-api/rust_playground_api:latest

docker_run_res=$?

Expand Down

0 comments on commit da26267

Please sign in to comment.