Serverless Rust-ing with Oracle Cloud Functions

Zeroing in on Serverless

Function - The cornerstone of every modular and beautifully designed piece of software.

Why Rust and running Rust Functions?

Overview of Oracle Cloud Functions

Prerequisites

  • Docker server
  • Latest version of Fn CLI. Fnproject is the base project of Oracle Fn and is maintained by the core team at the time of writing this story.

Steps

  • Using the init image to generate project and boilerplate code.
  • Create a Rust project.
  • Create a Dockerfile for deploying our code.
  • Running Fnproject locally.
  • Create an app and function using Fn CLI.
  • Build and deploy our function.
  • 10,000 feet testing via cURL.

Using the init image

Running init-image: metamemelord/fdk-rust:init
Executing docker command: run --rm -e FN_FUNCTION_NAME=simple-calc metamemelord/fdk-rust:init
func.yaml created.
.
├── Cargo.toml
├── Dockerfile
├── func.yaml
└── src
└── main.rs
1 directory, 4 files

Create a Rust project

  • Tokio — The runtime to provide an event loop for our server.
  • Serde — For serialization and deserialization.
  • FDK — We will be using git directly, alternatively you can use a dependency from crates.io.
#[derive(serde::Deserialize)]
enum Operation {
Add,
Sub,
Mul,
Div,
}
#[derive(serde::Deserialize)]
struct CalculatorInput {
operand1: f64,
operand2: f64,
operation: Operation,
}
#[derive(Serialize)]
struct CalculatorResult {
result: f64,
}
impl CalculatorResult {
fn new(result: f64) -> Self {
Self { result }
}
}
fn simple_calc(input: CalculatorInput) -> CalculatorResult {
match input.operation {
Operation::Add => CalculatorResult::new(input.operand1 + input.operand2),
Operation::Sub => CalculatorResult::new(input.operand1 - input.operand2),
Operation::Mul => CalculatorResult::new(input.operand1 * input.operand2),
Operation::Div => CalculatorResult::new(input.operand1 / input.operand2),
}
}
fn simple_calc(
_: &mut fdk::RuntimeContext,
input: CalculatorInput,
) -> fdk::Result<CalculatorResult> {
Ok(match input.operation {
Operation::Add => CalculatorResult::new(input.operand1 + input.operand2),
Operation::Sub => CalculatorResult::new(input.operand1 - input.operand2),
Operation::Mul => CalculatorResult::new(input.operand1 * input.operand2),
Operation::Div => CalculatorResult::new(input.operand1 / input.operand2),
})
}
#[tokio::main]
async fn main() {
let function = Function::run(simple_calc);
if let Err(e) = function.await {
eprintln!("{}", e);
};
}

Create a Dockerfile

Running Fnproject locally

2021/05/29 23:26:13 ¡¡¡ 'fn start' should NOT be used for PRODUCTION !!! see https://github.com/fnproject/fn-helm/
...
... // And a few more lines
CONTAINER ID   IMAGE                       COMMAND        CREATED         STATUS         PORTS                                                 NAMES
249baaec435c fnproject/fnserver:latest "./fnserver" 2 minutes ago Up 2 minutes 2375/tcp, 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp fnserver

Create an app and function using Fn CLI.

Successfully created app:  calculators
Dockerfile found. Using runtime 'docker'.
func.yaml created.

Build and deploy our function

Deploying simple-calc to app: calculators
Bumped to version 0.0.2
Building image simple-calc:0.0.2 ..............................................................................................................................
Updating function simple-calc using image simple-calc:0.0.2...
Successfully created function: simple-calc with simple-calc:0.0.2

10,000 feet testing via cURL

{
"annotations": {
"fnproject.io/fn/invokeEndpoint": "http://localhost:8080/invoke/<Function ID>"
},
"id": "<Function ID>",
// ... some metadata
}
curl --location --request POST 'http://localhost:8080/invoke/<Function ID>' \
--header 'Content-Type: application/json' \
--data-raw '{"operand1": 1.12,"operand2": 23.5,"operation": "Add"}'
curl --location --request POST 'http://localhost:8080/invoke/<Function ID>' \
--header 'Content-Type: application/xml' \
--data-raw '<CalculatorInput><operand1>2.23</operand1><operand2>1.21</operand2><operation>Sub</operation></CalculatorInput>'
curl --location --request POST 'http://localhost:8080/invoke/<Function ID>' \
--header 'Accept: application/xml' \
--header 'Content-Type: application/json' \
--data-raw '{"operand1": 1.1,"operand2": 23.5,"operation": "Mul"}'
curl --location --request POST 'http://localhost:8080/invoke/<Function ID>' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'operand1=10' \
--data-urlencode 'operand2=4' \
--data-urlencode 'operation=Div'

Further reading

Further exploration:

--

--

SMTS • Web Services • Rust/Go/Py/JS • Cloud & Distributed Systems

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Gaurav Saini

SMTS • Web Services • Rust/Go/Py/JS • Cloud & Distributed Systems