diff --git a/README.md b/README.md index cb2338358..3db34dc05 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,9 @@ docker run --rm -it --link functions:api -p 4000:4000 -e "API_URL=http://api:808 For more information, see: [https://github.com/treeder/functions-ui](https://github.com/treeder/functions-ui) -## Next up: Walk through the following [tutorial series](docs/tutorial/) --> +# Next up +### Check out the [Tutorial Series](examples/tutorial/). + It will demonstrate some of Oracle Functions capabilities through a series of exmaples. We'll try to show examples in most major languages. This is a great place to start! diff --git a/examples/README.md b/examples/README.md index 135c2daf1..caa49f893 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,4 +4,4 @@ This directory has a collection of example functions you can look at to learn mo ## Tutorial Series -The [Tutorial Series](tutorial/) will demonstrate some of Oracle Functions capabilities through a series of exmaples. We'll try to examples in most major languages. This is a great place to start! +The [Tutorial Series](tutorial/) will demonstrate some of Oracle Functions capabilities through a series of exmaples. We'll try to show examples in most major languages. This is a great place to start! diff --git a/examples/tutorial/hello/go/.gitignore b/examples/tutorial/hello/go/.gitignore new file mode 100644 index 000000000..47868d339 --- /dev/null +++ b/examples/tutorial/hello/go/.gitignore @@ -0,0 +1,7 @@ +vendor/ +/hello +/go +/app +/__uberscript__ + +func.yaml diff --git a/examples/tutorial/hello/go/README.md b/examples/tutorial/hello/go/README.md new file mode 100644 index 000000000..1a27e91f9 --- /dev/null +++ b/examples/tutorial/hello/go/README.md @@ -0,0 +1,56 @@ +# Tutorial 1: Go Function w/ Input (3 minutes) + +This example will show you how to test and deploy Go (Golang) code to Oracle Functions. It will also demonstrate passing data in through stdin. + +First, run the following commands to create, run, and deploy your function: + +```sh +# Initialize your function creating a func.yaml file +fn init /hello + +# Test your function. This will run inside a container exactly how it will on the server +fn run + +# Now try with an input +cat hello.payload.json | fn run + +# Deploy your functions to the Oracle Functions server (default localhost:8080) +# This will create a route to your function as well +fn deploy myapp +``` +Now call your function: + +```sh +curl http://localhost:8080/r/myapp/hello +``` +Or call from a browser: [http://localhost:8080/r/myapp/hello](http://localhost:8080/r/myapp/hello) + +And now with the JSON input: + +```sh +curl -H "Content-Type: application/json" -X POST -d @hello.payload.json http://localhost:8080/r/myapp/hello +``` + +That's it! + +# In Review + +1. We piped JSON data into the function at the command line +```sh +cat hello.payload.json | fn run +``` +2. We received our input through stdin +```go +json.NewDecoder(os.Stdin).Decode(p) +``` +3. We wrote our output to stdout +```go +fmt.Printf("Hello") +``` +4. We sent stderr to the server logs +```go +log.Println("here") +``` + +# Next Up +## [Tutorial 2: Input Parameters](examples/tutorial/params) \ No newline at end of file diff --git a/examples/tutorial/hello/go/func.go b/examples/tutorial/hello/go/func.go new file mode 100644 index 000000000..74ec37c55 --- /dev/null +++ b/examples/tutorial/hello/go/func.go @@ -0,0 +1,20 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" +) + +type Person struct { + Name string +} + +func main() { + p := &Person{Name: "World"} + json.NewDecoder(os.Stdin).Decode(p) + fmt.Printf("Hello %v!\n", p.Name) + + log.Println("---> stderr goes to the server logs.") +} diff --git a/examples/tutorial/hello/go/hello.payload.json b/examples/tutorial/hello/go/hello.payload.json new file mode 100644 index 000000000..97e136b69 --- /dev/null +++ b/examples/tutorial/hello/go/hello.payload.json @@ -0,0 +1,3 @@ +{ + "name": "Johnny" +} diff --git a/examples/tutorial/hello/node/.gitignore b/examples/tutorial/hello/node/.gitignore new file mode 100644 index 000000000..8977e475d --- /dev/null +++ b/examples/tutorial/hello/node/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +func.yaml +Dockerfile diff --git a/examples/tutorial/hello/node/README.md b/examples/tutorial/hello/node/README.md new file mode 100644 index 000000000..2a4baff97 --- /dev/null +++ b/examples/tutorial/hello/node/README.md @@ -0,0 +1,41 @@ +## Quick Example for a NodeJS Function (4 minutes) + +This example will show you how to test and deploy a Node function to Oracle Functions. + +```sh +# create your func.yaml file +fn init /hello +# build the function +fn build +# test it +cat hello.payload.json | fn run +# push it to Docker Hub +fn push +# Create a route to this function on Oracle Functions +fn routes create myapp /hello +``` + +Now surf to: http://localhost:8080/r/myapp/hello + +## Dependencies + +Create a [package.json](https://docs.npmjs.com/getting-started/using-a-package.json) file in your functions directory. + +Run: + +```sh +docker run --rm -v "$PWD":/function -w /function funcy/node:dev npm install +``` + +Then everything should work. + +For example, using the `package.json` file in this directory which includes the [request](https://www.npmjs.com/package/request) package, you can add this to func.js and it will work: + +```js +var request = require('request'); +request('http://www.google.com', function (error, response, body) { + if (!error && response.statusCode == 200) { + console.log(body) // Show the HTML for the Google homepage. + } +}) +``` diff --git a/examples/tutorial/hello/node/func.js b/examples/tutorial/hello/node/func.js new file mode 100644 index 000000000..356544e81 --- /dev/null +++ b/examples/tutorial/hello/node/func.js @@ -0,0 +1,9 @@ +name = "World"; +fs = require('fs'); +try { + obj = JSON.parse(fs.readFileSync('/dev/stdin').toString()) + if (obj.name != "") { + name = obj.name + } +} catch(e) {} +console.log("Hello", name, "from Node!"); diff --git a/examples/tutorial/hello/node/hello.payload.json b/examples/tutorial/hello/node/hello.payload.json new file mode 100644 index 000000000..97e136b69 --- /dev/null +++ b/examples/tutorial/hello/node/hello.payload.json @@ -0,0 +1,3 @@ +{ + "name": "Johnny" +} diff --git a/examples/tutorial/hello/node/package.json b/examples/tutorial/hello/node/package.json new file mode 100644 index 000000000..f332baddb --- /dev/null +++ b/examples/tutorial/hello/node/package.json @@ -0,0 +1,7 @@ +{ + "name": "my-awesome-func", + "version": "1.0.0", + "dependencies": { + "request": "^2.78.0" + } +} \ No newline at end of file diff --git a/examples/tutorial/hello/php/.gitignore b/examples/tutorial/hello/php/.gitignore new file mode 100644 index 000000000..48b8bf907 --- /dev/null +++ b/examples/tutorial/hello/php/.gitignore @@ -0,0 +1 @@ +vendor/ diff --git a/examples/tutorial/hello/php/Dockerfile b/examples/tutorial/hello/php/Dockerfile new file mode 100644 index 000000000..1da497e06 --- /dev/null +++ b/examples/tutorial/hello/php/Dockerfile @@ -0,0 +1,6 @@ +FROM funcy/php + +WORKDIR /app +ADD . /app + +ENTRYPOINT ["php", "hello.php"] diff --git a/examples/tutorial/hello/php/README.md b/examples/tutorial/hello/php/README.md new file mode 100644 index 000000000..1ccc9b1d1 --- /dev/null +++ b/examples/tutorial/hello/php/README.md @@ -0,0 +1,48 @@ +## Quick Example for a PHP Function (4 minutes) + +This example will show you how to test and deploy Go (Golang) code to Oracle Functions. + +### 1. Prepare the `func.yaml` file: + +At func.yaml you will find: + +```yml +name: USERNAME/hello +version: 0.0.1 +path: /hello +build: +- docker run --rm -v "$PWD":/worker -w /worker funcy/php:dev composer install +``` + +The important step here is to ensure you replace `USERNAME` with your Docker Hub account name. Some points of note: +the application name is `phpapp` and the route for incoming requests is `/hello`. These informations are relevant for +the moment you try to test this function. + +### 2. Build: + +```sh +# build the function +fn build +# test it +cat hello.payload.json | fn run +# push it to Docker Hub +fn push +# Create a route to this function on Oracle Functions +fn routes create phpapp /hello +``` + +`-v` is optional, but it allows you to see how this function is being built. + +### 3. Queue jobs for your function + +Now you can start jobs on your function. Let's quickly queue up a job to try it out. + +```sh +cat hello.payload.json | fn call phpapp /hello +``` + +Here's a curl example to show how easy it is to do in any language: + +```sh +curl -H "Content-Type: application/json" -X POST -d @hello.payload.json http://localhost:8080/r/phpapp/hello +``` \ No newline at end of file diff --git a/examples/tutorial/hello/php/composer.json b/examples/tutorial/hello/php/composer.json new file mode 100644 index 000000000..df8dd9cf8 --- /dev/null +++ b/examples/tutorial/hello/php/composer.json @@ -0,0 +1,4 @@ +{ + "require": { + } +} diff --git a/examples/tutorial/hello/php/func.yaml b/examples/tutorial/hello/php/func.yaml new file mode 100644 index 000000000..027b012ee --- /dev/null +++ b/examples/tutorial/hello/php/func.yaml @@ -0,0 +1,5 @@ +name: USERNAME/hello +version: 0.0.1 +path: /hello +build: +- docker run --rm -v "$PWD":/worker -w /worker funcy/php:dev composer install diff --git a/examples/tutorial/hello/php/hello.payload.json b/examples/tutorial/hello/php/hello.payload.json new file mode 100644 index 000000000..97e136b69 --- /dev/null +++ b/examples/tutorial/hello/php/hello.payload.json @@ -0,0 +1,3 @@ +{ + "name": "Johnny" +} diff --git a/examples/tutorial/hello/php/hello.php b/examples/tutorial/hello/php/hello.php new file mode 100644 index 000000000..7828f1f95 --- /dev/null +++ b/examples/tutorial/hello/php/hello.php @@ -0,0 +1,10 @@ + 0.14.0' +gem 'json', '> 1.8.2' diff --git a/examples/tutorial/hello/ruby/README.md b/examples/tutorial/hello/ruby/README.md new file mode 100644 index 000000000..05ef81eea --- /dev/null +++ b/examples/tutorial/hello/ruby/README.md @@ -0,0 +1,44 @@ +## Quick Example for a Ruby Function (4 minutes) + +This example will show you how to test and deploy a Ruby function to Oracle Functions. + +```sh +# create your func.yaml file +fn init /hello +# install dependencies, we need the json gem to run this +docker run --rm -it -v ${pwd}:/worker -w /worker funcy/ruby:dev bundle install --standalone --clean +# build the function +fn build +# test it +cat hello.payload.json | fn run +# push it to Docker Hub +fn push +# Create a route to this function on Oracle Functions +fn routes create myapp /hello +``` + +Now surf to: http://localhost:8080/r/myapp/hello + +## Dependencies + +Create a [Gemfile](http://bundler.io/gemfile.html) file in your function directory, then run: + +```sh +docker run --rm -it -v ${pwd}:/worker -w /worker funcy/ruby:dev bundle install --standalone --clean +``` + +Ruby doesn't pick up the gems automatically, so you'll have to add this to the top of your `func.rb` file: + +```ruby +require_relative 'bundle/bundler/setup' +``` + +Open `func.rb` to see it in action. + +To update dependencies: + +```sh +docker run --rm -it -v ${pwd}:/worker -w /worker funcy/ruby:dev bundle update +# then install again to vendor them +docker run --rm -it -v ${pwd}:/worker -w /worker funcy/ruby:dev bundle update +``` diff --git a/examples/tutorial/hello/ruby/func.rb b/examples/tutorial/hello/ruby/func.rb new file mode 100644 index 000000000..a16a04030 --- /dev/null +++ b/examples/tutorial/hello/ruby/func.rb @@ -0,0 +1,12 @@ +require_relative 'bundle/bundler/setup' +require 'json' + +name = "World" + +payload = STDIN.read +if payload != "" + payload = JSON.parse(payload) + name = payload['name'] +end + +puts "Hello #{name} from Ruby!" diff --git a/examples/tutorial/hello/ruby/hello.payload.json b/examples/tutorial/hello/ruby/hello.payload.json new file mode 100644 index 000000000..97e136b69 --- /dev/null +++ b/examples/tutorial/hello/ruby/hello.payload.json @@ -0,0 +1,3 @@ +{ + "name": "Johnny" +} diff --git a/examples/tutorial/hello/rust/Cargo.toml b/examples/tutorial/hello/rust/Cargo.toml new file mode 100644 index 000000000..7c3608e1d --- /dev/null +++ b/examples/tutorial/hello/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "func" +version = "0.1.0" +authors = ["Seif Lotfy "] + +[dependencies] diff --git a/examples/tutorial/hello/rust/README.md b/examples/tutorial/hello/rust/README.md new file mode 100644 index 000000000..895f3ba51 --- /dev/null +++ b/examples/tutorial/hello/rust/README.md @@ -0,0 +1,33 @@ +# Using rust with functions + +The easiest way to create a function in rust is via ***cargo*** and ***fn***. + +## Prerequisites + +First create an epty rust project as follows: + +```bash +cargo init --name func --bin +``` + +Make sure the project name is ***func*** and is of type ***bin***. Now just edit your code, once done you can create a function. + +## Creating a function + +Simply run + +```bash +fn init --runtime=rust / +``` + +This will create the ```func.yaml``` file required by functions, which can be built by running: + +```bash +fn build +``` + +## Testing + +```bash +fn run +``` diff --git a/examples/tutorial/hello/rust/src/main.rs b/examples/tutorial/hello/rust/src/main.rs new file mode 100644 index 000000000..020c757b1 --- /dev/null +++ b/examples/tutorial/hello/rust/src/main.rs @@ -0,0 +1,10 @@ +use std::io; +use std::io::Read; + +fn main() { + let mut buffer = String::new(); + let stdin = io::stdin(); + if stdin.lock().read_to_string(&mut buffer).is_ok() { + println!("Hello {}", buffer.trim()); + } +} diff --git a/examples/tutorial/hotfunctions/http/func.go b/examples/tutorial/hotfunctions/http/func.go new file mode 100644 index 000000000..cba961f2e --- /dev/null +++ b/examples/tutorial/hotfunctions/http/func.go @@ -0,0 +1,45 @@ +package main + +import ( + "bufio" + "bytes" + "fmt" + "io/ioutil" + "net/http" + "os" + "strconv" +) + +func main() { + for { + res := http.Response{ + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + StatusCode: 200, + Status: "OK", + } + + r := bufio.NewReader(os.Stdin) + req, err := http.ReadRequest(r) + + var buf bytes.Buffer + if err != nil { + res.StatusCode = 500 + res.Status = http.StatusText(res.StatusCode) + fmt.Fprintln(&buf, err) + } else { + l, _ := strconv.Atoi(req.Header.Get("Content-Length")) + p := make([]byte, l) + r.Read(p) + fmt.Fprintf(&buf, "Hello %s\n", p) + for k, vs := range req.Header { + fmt.Fprintf(&buf, "ENV: %s %#v\n", k, vs) + } + } + + res.Body = ioutil.NopCloser(&buf) + res.ContentLength = int64(buf.Len()) + res.Write(os.Stdout) + } +} diff --git a/examples/tutorial/hotfunctions/http/hotroute.json b/examples/tutorial/hotfunctions/http/hotroute.json new file mode 100644 index 000000000..af7e65172 --- /dev/null +++ b/examples/tutorial/hotfunctions/http/hotroute.json @@ -0,0 +1,9 @@ +{"route":{ + "app_name": "myapp", + "path": "/hot", + "image": "USERNAME/hchttp", + "memory": 64, + "type": "sync", + "config": null, + "format": "http" +}} diff --git a/examples/tutorial/index.md b/examples/tutorial/index.md new file mode 100644 index 000000000..530d72ddc --- /dev/null +++ b/examples/tutorial/index.md @@ -0,0 +1,7 @@ + +# Tutorial Series + +1. [Tutorial 1](examples/tutorial/hello): Learn the basics about sending data into your function +2. [Tutorial 2](examples/tutorial/params): Learn how to get parameters from a web request +3. [Tutorial 3](examples/tutorial/hotfunctions): Write your first HotFunction (stays alive to minimze latency between requests) +4. [Tutorial 4](): TBD \ No newline at end of file diff --git a/examples/tutorial/params/func.go b/examples/tutorial/params/func.go new file mode 100644 index 000000000..38b34dc59 --- /dev/null +++ b/examples/tutorial/params/func.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "log" + "net/url" + "os" +) + +func main() { + s := os.Getenv("REQUEST_URL") + + fmt.Printf("REQUEST_URL --> %v\n\n", s) + + u, err := url.Parse(s) + if err != nil { + log.Fatal(err) + } + + m, _ := url.ParseQuery(u.RawQuery) + + if len(m) == 0 { + fmt.Println("Try adding some URL params like &id=123") + } else { + for k, v := range m { + fmt.Printf("found param: %v, val: %v\n", k, v[0]) + } + } + +} diff --git a/examples/tutorial/params/func.yaml b/examples/tutorial/params/func.yaml new file mode 100644 index 000000000..f26e7a126 --- /dev/null +++ b/examples/tutorial/params/func.yaml @@ -0,0 +1,6 @@ +name: carimura2/fn3_request +version: 0.0.6 +runtime: go +entrypoint: ./func +path: /fn3 +max_concurrency: 1