diff --git a/Gopkg.lock b/Gopkg.lock index bde9974e2..acdec870d 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -509,4 +509,4 @@ memo = "f5b1c7d56bf54bc74276d2d9f46275abedb0dce21386c1418a79e1b0ea972c25" branch = "v2" name = "gopkg.in/yaml.v2" packages = ["."] - revision = "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b" + revision = "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b" \ No newline at end of file diff --git a/README.md b/README.md index 4d982d43e..6492fb87d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,9 @@ -# NONAME... :( +# Oracle Functions -[![CircleCI](https://circleci.com/gh/treeder/functions.svg?style=svg)](https://circleci.com/gh/treeder/functions) -[![GoDoc](https://godoc.org/github.com/treeder/functions?status.svg)](https://godoc.org/github.com/treeder/functions) + -Welcome to Oracle Functions! The open source serverless platform. - -## What is Oracle Functions? - -Oracle Functions is an open source [serverless](serverless.md) platform, or as we like to refer to it, Functions as a -Service (FaaS) platform that you can run anywhere. +Oracle Functions is an event-driven, open source, [functions-as-a-service](serverless.md) compute +platform that you can run anywhere. Some of it's key features: * Write once * [Any language](docs/faq.md#which-languages-are-supported) @@ -19,31 +14,32 @@ Service (FaaS) platform that you can run anywhere. * Easy to use [for developers](docs/README.md#for-developers) * Easy to manage [for operators](docs/README.md#for-operators) * Written in [Go](https://golang.org) +* Simple yet powerful extensibility -## Join Our Community -TODO: Slack or Discord community. - -## Quickstart - -This guide will get you up and running in a few minutes. - -### Prequisites +## Prequisites * Docker 17.05 or later installed and running * Logged into Docker Hub (`docker login`) -# UNTIL THIS IS PUBLIC, YOU'LL NEED TO BUILD AND RUN THE CODE FROM THIS REPO +## Usage + +### Installation + +NOTE: The following instructions apply while the project is a private repo. This will +build the Functions server and the CLI tool directly from the repo instead of +using pre-built containers. Once the project is public, these steps will be unnecessary. ```sh -# install cli tool +# Build and Install CLI tool cd fn make dep # just once make install -# Start server: + +# Build and Run Functions Server cd .. make dep # just once -make run +make run # will build as well ``` -### Write a Function +### Your First Function -Functions are small, bite sized bits of code that do one simple thing. Forget about monoliths when using functions, -just focus on the task that you want the function to perform. +Functions are small but powerful blocks of code that generally do one simple thing. Forget about monoliths when using functions, just focus on the task that you want the function to perform. -The following is a Go function that just returns "Hello ${NAME}!": +The following is a simple Go program that outputs a string to STDOUT. Copy and paste the code below into a file called `func.go`. Currently the function must be named func.your_language_extention (ie func.go, func.js, etc.) ```go package main import ( - "encoding/json" "fmt" - "os" ) -type Person struct { - Name string -} - func main() { - p := &Person{Name: "World"} - json.NewDecoder(os.Stdin).Decode(p) - fmt.Printf("Hello %v!", p.Name) + fmt.Println("Hello from Oracle Functions!") } ``` -Copy and paste the code above into a file called `func.go`, then run the following commands to build your function -and deploy it. +Now run the following CLI commands: ```sh -# Initilize your function, replace $USERNAME with your Docker Hub username. -fn init $USERNAME/hello -# Test it - you can pass data into it too by piping it in, eg: `cat hello.payload.json | fn run` +# Initialize your function +# This detects your runtime from the code above and creates a func.yaml +fn init /hello + +# Test your function +# This will run inside a container exactly how it will on the server fn run -# Once it's ready, deploy it to your functions server (default localhost:8080) -fn apps create myapp + +# Deploy your functions to the Oracle Functions server (default localhost:8080) +# This will create a route to your function as well fn deploy myapp ``` @@ -117,59 +107,39 @@ Now you can call your function: curl http://localhost:8080/r/myapp/hello ``` -Or surf to it: http://localhost:8080/r/myapp/hello +Or in a browser: [http://localhost:8080/r/myapp/hello](http://localhost:8080/r/myapp/hello) -To update your function: +That's it! You just deployed your first function and called it. Now to update your function +you can update your code and run `fn deploy myapp` again. -```sh -# Just update your code and run: -fn deploy myapp -``` +## To Learn More -See the [documentation](docs/README.md) for more information. And you can find a bunch of examples in various languages in the [examples](examples/) directory. You can also -write your functions in AWS's [Lambda format](docs/lambda/README.md). +- Visit our Functions [Tutorial Series](examples/tutorial/) +- See our [full documentation](docs/README.md) +- View all of our [examples](/examples) +- You can also write your functions in AWS [Lambda format](docs/lambda/README.md) -## Functions UI +## Get Involved + +- TODO: Slack or Discord community +- Learn how to [contribute](CONTRIBUTING.md) +- See [milestones](https://gitlab.oracledx.com/odx/functions/milestones) for detailed issues + + +## User Interface + +This is the graphical user interface for Oracle Functions. It is currently not buildable. ```sh docker run --rm -it --link functions:api -p 4000:4000 -e "API_URL=http://api:8080" treeder/functions-ui ``` -For more information, see: https://github.com/treeder/functions-ui +For more information, see: [https://github.com/treeder/functions-ui](https://github.com/treeder/functions-ui) -## Writing Functions -See [Writing Functions](docs/writing.md). +# Next up -And you can find a bunch of examples in the [/examples](/examples) directory. +### Check out the [Tutorial Series](examples/tutorial/). -## More Documentation + 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! -See [docs/](docs/README.md) for full documentation. - -## Roadmap - -These are the high level roadmap goals. See [milestones](https://github.com/treeder/functions/milestones) for detailed issues. - -* ~~Alpha 1 - November 2016~~ - * Initial release of base framework - * Lambda support -* ~~Alpha 2 - December 2016~~ - * Streaming input for hot functions #214 - * Logging endpoint(s) for per function debugging #263 -* Beta 1 - January 2017 - * Smart Load Balancer #151 -* Beta 2 - February 2017 - * Cron like scheduler #100 -* GA - March 2017 - -## Support - -You can get community support via: - -* [Stack Overflow](http://stackoverflow.com/questions/tagged/functions) -* [Slack](http://get.iron.io/open-slack) - -## Want to contribute to Oracle Functions? - -See [contributing](CONTRIBUTING.md). diff --git a/api/server/server.go b/api/server/server.go index 4bb6af58a..73002ee95 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -211,6 +211,20 @@ func (s *Server) startGears(ctx context.Context) { if err != nil { logrus.WithError(err).Fatalln("Failed to serve functions API.") } + + const runHeader = ` + ____ __ + / __ \_________ ______/ /__ + / / / / ___/ __ / ___/ / _ \ + / /_/ / / / /_/ / /__/ / __/ + \_________ \__,_/\___/_/\____ + / ____/_ __ ___ _____/ /_( )___ ____ _____ + / /_ / / / / __ \/ ___/ __/ / __ \/ __ \/ ___/ + / __/ / /_/ / / / / /__/ /_/ / /_/ / / / (__ ) + /_/ \____/_/ /_/\___/\__/_/\____/_/ /_/____/ +` + + logrus.Infof(runHeader) logrus.Infof("Serving Functions API on address `%s`", listen) svr := &supervisor.Supervisor{ diff --git a/examples/README.md b/examples/README.md index 3b6ea228f..caa49f893 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,9 +1,7 @@ # Example Functions -This directory has a collection of example functions you can look at to learn more about how to write them -or just copy one and build on it to get started faster. +This directory has a collection of example functions you can look at to learn more about how to write them or just copy one and build on it to get started faster. -## Hello World Examples +## Tutorial Series -The [Hello World examples](hello/) are the most basic functions you can write and we'll try to have an example in most major languages. -This is a good place to start and good examples to copy and build upon. +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/hello/go/README.md b/examples/hello/go/README.md deleted file mode 100644 index ced7b8d99..000000000 --- a/examples/hello/go/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Quick Example for a Go Function (3 minutes) - -This example will show you how to test and deploy Go (Golang) code 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 you can call your function on Oracle Functions: - -```sh -curl -H "Content-Type: application/json" -X POST -d @hello.payload.json http://localhost:8080/r/myapp/hello -``` - -## Dependencies - -Be sure you're dependencies are in the `vendor/` directory and that's it. - diff --git a/examples/hello/node/README.md b/examples/hello/node/README.md deleted file mode 100644 index 2a4baff97..000000000 --- a/examples/hello/node/README.md +++ /dev/null @@ -1,41 +0,0 @@ -## 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/hello/php/.gitignore b/examples/hello/php/.gitignore deleted file mode 100644 index 48b8bf907..000000000 --- a/examples/hello/php/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vendor/ diff --git a/examples/hello/php/Dockerfile b/examples/hello/php/Dockerfile deleted file mode 100644 index 1da497e06..000000000 --- a/examples/hello/php/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM funcy/php - -WORKDIR /app -ADD . /app - -ENTRYPOINT ["php", "hello.php"] diff --git a/examples/hello/php/README.md b/examples/hello/php/README.md deleted file mode 100644 index 1ccc9b1d1..000000000 --- a/examples/hello/php/README.md +++ /dev/null @@ -1,48 +0,0 @@ -## 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/hello/php/composer.json b/examples/hello/php/composer.json deleted file mode 100644 index df8dd9cf8..000000000 --- a/examples/hello/php/composer.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "require": { - } -} diff --git a/examples/hello/php/func.yaml b/examples/hello/php/func.yaml deleted file mode 100644 index 027b012ee..000000000 --- a/examples/hello/php/func.yaml +++ /dev/null @@ -1,5 +0,0 @@ -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/hello/ruby/README.md b/examples/hello/ruby/README.md deleted file mode 100644 index 05ef81eea..000000000 --- a/examples/hello/ruby/README.md +++ /dev/null @@ -1,44 +0,0 @@ -## 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/README.md b/examples/tutorial/README.md new file mode 100644 index 000000000..ab947e8ae --- /dev/null +++ b/examples/tutorial/README.md @@ -0,0 +1,15 @@ + +# Tutorial Series + +Welcome to the Oracle Functions Tutorial Series, the best way to get started with Oracle Functions and serverless computing. In the following +tutorials, we'll gradually introduce many of the key features of Oracle Functions. + + +**[Tutorial 1](examples/tutorial/hello)** +: Learn the basics about sending data into your function + +**[Tutorial 2](examples/tutorial/params)**: Learn how to get parameters from a web request + +**[Tutorial 3](examples/tutorial/hotfunctions)**: Write your first HotFunction (stays alive to minimize latency between requests) + +**[Tutorial 4]()**: TBD diff --git a/examples/hello/go/.gitignore b/examples/tutorial/hello/go/.gitignore similarity index 100% rename from examples/hello/go/.gitignore rename to examples/tutorial/hello/go/.gitignore diff --git a/examples/tutorial/hello/go/README.md b/examples/tutorial/hello/go/README.md new file mode 100644 index 000000000..1a87a0343 --- /dev/null +++ b/examples/tutorial/hello/go/README.md @@ -0,0 +1,65 @@ +# 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: + +```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! + +### Note on Dependencies + +In Go, simply put them all in the `vendor/` directory. + +# 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 function 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/hello/go/func.go b/examples/tutorial/hello/go/func.go similarity index 64% rename from examples/hello/go/func.go rename to examples/tutorial/hello/go/func.go index bf0518407..74ec37c55 100644 --- a/examples/hello/go/func.go +++ b/examples/tutorial/hello/go/func.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "fmt" + "log" "os" ) @@ -13,5 +14,7 @@ type Person struct { func main() { p := &Person{Name: "World"} json.NewDecoder(os.Stdin).Decode(p) - fmt.Printf("Hello %v!", p.Name) + fmt.Printf("Hello %v!\n", p.Name) + + log.Println("---> stderr goes to the server logs.") } diff --git a/examples/hello/go/hello.payload.json b/examples/tutorial/hello/go/hello.payload.json similarity index 100% rename from examples/hello/go/hello.payload.json rename to examples/tutorial/hello/go/hello.payload.json diff --git a/examples/hello/node/.gitignore b/examples/tutorial/hello/node/.gitignore similarity index 100% rename from examples/hello/node/.gitignore rename to examples/tutorial/hello/node/.gitignore diff --git a/examples/tutorial/hello/node/README.md b/examples/tutorial/hello/node/README.md new file mode 100644 index 000000000..83903eb00 --- /dev/null +++ b/examples/tutorial/hello/node/README.md @@ -0,0 +1,89 @@ +# Tutorial 1: NodeJS Function w/ Input (3 minutes) + +This example will show you how to test and deploy Node code to Oracle Functions. It will also demonstrate passing data in through stdin. + +### First, run the following commands: + +```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. It will also install and vendor dependencies from Gemfile +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! + +### Note on 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. + } +}) +``` + + +# 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 function input through **stdin** + ```node + obj = JSON.parse(fs.readFileSync('/dev/stdin').toString()) + ``` + +3. We wrote our output to **stdout** + ```node + console.log + ``` + +4. We sent **stderr** to the server logs + ```node + console.error + ``` + + +# Next Up +## [Tutorial 2: Input Parameters](examples/tutorial/params) + + + diff --git a/examples/hello/node/func.js b/examples/tutorial/hello/node/func.js similarity index 79% rename from examples/hello/node/func.js rename to examples/tutorial/hello/node/func.js index 356544e81..5d78d1143 100644 --- a/examples/hello/node/func.js +++ b/examples/tutorial/hello/node/func.js @@ -7,3 +7,5 @@ try { } } catch(e) {} console.log("Hello", name, "from Node!"); + +console.error("Stderr goes to the server logs..."); diff --git a/examples/hello/node/hello.payload.json b/examples/tutorial/hello/node/hello.payload.json similarity index 100% rename from examples/hello/node/hello.payload.json rename to examples/tutorial/hello/node/hello.payload.json diff --git a/examples/hello/node/package.json b/examples/tutorial/hello/node/package.json similarity index 100% rename from examples/hello/node/package.json rename to examples/tutorial/hello/node/package.json diff --git a/examples/tutorial/hello/php/.gitignore b/examples/tutorial/hello/php/.gitignore new file mode 100644 index 000000000..fa3f387e3 --- /dev/null +++ b/examples/tutorial/hello/php/.gitignore @@ -0,0 +1,3 @@ +vendor/ +func.yaml +composer.lock diff --git a/examples/tutorial/hello/php/README.md b/examples/tutorial/hello/php/README.md new file mode 100644 index 000000000..7c15ea6d4 --- /dev/null +++ b/examples/tutorial/hello/php/README.md @@ -0,0 +1,94 @@ +# Tutorial 1: PHP Function w/ Input (3 minutes) + +This example will show you how to test and deploy PHP code to Oracle Functions. It will also demonstrate passing data in through stdin. + +### First, run the following commands: + +```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. It will also install and vendor dependencies from Gemfile +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! + +### Note on Dependencies + +In PHP, you can create a [composer](https://getcomposer.org/) file in your function directory, then run: + +```sh +fn build +``` + +This will rebuild your gems and vendor them. PHP doesn't pick them up automatically, so you'll have to add this to the top of your `func.php` file: + +```php +require 'vendor/autoload.php'; +``` + +Open `func.php` to see it in action. + + +### 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 +``` + + + +# 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 function input through **stdin** + ```php + $payload = json_decode(file_get_contents("php://stdin"), true); + ``` + +3. We wrote our output to **stdout** + ```php + echo "Hello World!\n"; + ``` + +4. We sent **stderr** to the server logs + ```php + fwrite(STDERR, "--> this will go to stderr (server logs)\n"); + ``` + +5. We added PHP dependencies and enabled them using: + ```php + require 'vendor/autoload.php'; + ``` + + +# Next Up +## [Tutorial 2: Input Parameters](examples/tutorial/params) diff --git a/examples/tutorial/hello/php/composer.json b/examples/tutorial/hello/php/composer.json new file mode 100644 index 000000000..6f1595fec --- /dev/null +++ b/examples/tutorial/hello/php/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "monolog/monolog": "1.0.*" + } +} diff --git a/examples/hello/php/hello.php b/examples/tutorial/hello/php/func.php similarity index 57% rename from examples/hello/php/hello.php rename to examples/tutorial/hello/php/func.php index 7828f1f95..50781499f 100644 --- a/examples/hello/php/hello.php +++ b/examples/tutorial/hello/php/func.php @@ -1,10 +1,11 @@ this will go to stderr (server logs)\n"); stream_set_blocking(STDIN, 0); $payload = json_decode(file_get_contents("php://stdin"), true); if (isset($payload['name'])) { - echo "Hello ", $payload['name'],"!\n\n"; + echo "Hello ", $payload['name'],"!\n"; } else { - echo "Hello World!\n\n"; + echo "Hello World!\n"; } diff --git a/examples/tutorial/hello/php/func.yaml b/examples/tutorial/hello/php/func.yaml new file mode 100644 index 000000000..6036f79c7 --- /dev/null +++ b/examples/tutorial/hello/php/func.yaml @@ -0,0 +1,6 @@ +name: carimura2/hello +version: 0.0.2 +runtime: php +entrypoint: php func.php +path: /hello +max_concurrency: 1 diff --git a/examples/hello/php/hello.payload.json b/examples/tutorial/hello/php/hello.payload.json similarity index 100% rename from examples/hello/php/hello.payload.json rename to examples/tutorial/hello/php/hello.payload.json diff --git a/examples/hello/python/.gitignore b/examples/tutorial/hello/python/.gitignore similarity index 100% rename from examples/hello/python/.gitignore rename to examples/tutorial/hello/python/.gitignore diff --git a/examples/hello/python/Dockerfile b/examples/tutorial/hello/python/Dockerfile similarity index 100% rename from examples/hello/python/Dockerfile rename to examples/tutorial/hello/python/Dockerfile diff --git a/examples/hello/python/README.md b/examples/tutorial/hello/python/README.md similarity index 100% rename from examples/hello/python/README.md rename to examples/tutorial/hello/python/README.md diff --git a/examples/hello/python/hello.payload.json b/examples/tutorial/hello/python/hello.payload.json similarity index 100% rename from examples/hello/python/hello.payload.json rename to examples/tutorial/hello/python/hello.payload.json diff --git a/examples/hello/python/hello.py b/examples/tutorial/hello/python/hello.py similarity index 100% rename from examples/hello/python/hello.py rename to examples/tutorial/hello/python/hello.py diff --git a/examples/hello/python/requirements.txt b/examples/tutorial/hello/python/requirements.txt similarity index 100% rename from examples/hello/python/requirements.txt rename to examples/tutorial/hello/python/requirements.txt diff --git a/examples/hello/ruby/.gitignore b/examples/tutorial/hello/ruby/.gitignore similarity index 100% rename from examples/hello/ruby/.gitignore rename to examples/tutorial/hello/ruby/.gitignore diff --git a/examples/hello/ruby/Gemfile b/examples/tutorial/hello/ruby/Gemfile similarity index 100% rename from examples/hello/ruby/Gemfile rename to examples/tutorial/hello/ruby/Gemfile diff --git a/examples/tutorial/hello/ruby/README.md b/examples/tutorial/hello/ruby/README.md new file mode 100644 index 000000000..f348f9699 --- /dev/null +++ b/examples/tutorial/hello/ruby/README.md @@ -0,0 +1,96 @@ +# Tutorial 1: Ruby Function w/ Input (3 minutes) + +This example will show you how to test and deploy Ruby code to Oracle Functions. It will also demonstrate passing data in through stdin. + +### First, run the following commands: + +```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. It will also install and vendor dependencies from Gemfile +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! + +### Note on Dependencies + +In Ruby, we create a [Gemfile](http://bundler.io/gemfile.html) file in your function directory, then run: + +```sh +fn build +``` + +This will rebuild your gems and vendor them. 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 +# Let's run bundle update +docker run --rm -it -v ${PWD}:/worker -w /worker funcy/ruby:dev bundle update + +# Then bundle install again to vendor them +docker run --rm -it -v ${PWD}:/worker -w /worker funcy/ruby:dev bundle install --standalone --clean +``` + +# 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 function input through **stdin** + ```ruby + payload = STDIN.read + ``` + +3. We wrote our output to **stdout** + ```ruby + puts "Hello #{name} from Ruby!" + ``` + +4. We sent **stderr** to the server logs + ```ruby + STDERR.puts + ``` + +5. We enabled our Ruby gem dependencies using `require_relative` + ```ruby + require_relative 'bundle/bundler/setup' + ``` + + +# Next Up +## [Tutorial 2: Input Parameters](examples/tutorial/params) + + + diff --git a/examples/hello/ruby/func.rb b/examples/tutorial/hello/ruby/func.rb similarity index 71% rename from examples/hello/ruby/func.rb rename to examples/tutorial/hello/ruby/func.rb index a16a04030..6399af036 100644 --- a/examples/hello/ruby/func.rb +++ b/examples/tutorial/hello/ruby/func.rb @@ -9,4 +9,6 @@ if payload != "" name = payload['name'] end -puts "Hello #{name} from Ruby!" +puts "Hello #{name}!" + +STDERR.puts "---> STDERR goes to server logs" diff --git a/examples/hello/ruby/hello.payload.json b/examples/tutorial/hello/ruby/hello.payload.json similarity index 100% rename from examples/hello/ruby/hello.payload.json rename to examples/tutorial/hello/ruby/hello.payload.json diff --git a/examples/hello/rust/Cargo.toml b/examples/tutorial/hello/rust/Cargo.toml similarity index 100% rename from examples/hello/rust/Cargo.toml rename to examples/tutorial/hello/rust/Cargo.toml diff --git a/examples/hello/rust/README.md b/examples/tutorial/hello/rust/README.md similarity index 100% rename from examples/hello/rust/README.md rename to examples/tutorial/hello/rust/README.md diff --git a/examples/hello/rust/src/main.rs b/examples/tutorial/hello/rust/src/main.rs similarity index 100% rename from examples/hello/rust/src/main.rs rename to examples/tutorial/hello/rust/src/main.rs diff --git a/examples/hotfunctions/http/func.go b/examples/tutorial/hotfunctions/http/func.go similarity index 100% rename from examples/hotfunctions/http/func.go rename to examples/tutorial/hotfunctions/http/func.go diff --git a/examples/hotfunctions/http/hotroute.json b/examples/tutorial/hotfunctions/http/hotroute.json similarity index 100% rename from examples/hotfunctions/http/hotroute.json rename to examples/tutorial/hotfunctions/http/hotroute.json 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 diff --git a/fn/Gopkg.toml b/fn/Gopkg.toml index ba6c8c910..0a5367f05 100644 --- a/fn/Gopkg.toml +++ b/fn/Gopkg.toml @@ -52,6 +52,7 @@ ignored = ["github.com/treeder/functions", "github.com/treeder/functions/api/version"] [[dependencies]] + branch = "master" name = "github.com/aws/aws-sdk-go" version = "^1.8.23" diff --git a/fn/apps.go b/fn/apps.go index 54617458d..729637a2f 100644 --- a/fn/apps.go +++ b/fn/apps.go @@ -145,7 +145,20 @@ func (a *appsCmd) create(c *cli.Context) error { return fmt.Errorf("unexpected error: %v", err) } - fmt.Println(resp.Payload.App.Name, "created") + const createHeader = ` + ____ __ + / __ \_________ ______/ /__ + / / / / ___/ __ / ___/ / _ \ +/ /_/ / / / /_/ / /__/ / __/ +\_________ \__,_/\___/_/\____ _ + / ____/_ ______ _____/ /_(_)___ ____ _____ + / /_ / / / / __ \/ ___/ __/ / __ \/ __ \/ ___/ + / __/ / /_/ / / / / /__/ /_/ / /_/ / / / (__ ) +/_/ \____/_/ /_/\___/\__/_/\____/_/ /_/____/ +` + + fmt.Println(createHeader) + fmt.Println("Successfully created app: ", resp.Payload.App.Name) return nil } diff --git a/fn/init.go b/fn/init.go index 4e46293f8..18142a58b 100644 --- a/fn/init.go +++ b/fn/init.go @@ -17,19 +17,20 @@ import ( "strings" - "gitlab.oracledx.com/odx/functions/fn/langs" "github.com/urfave/cli" + "gitlab.oracledx.com/odx/functions/fn/langs" ) var ( fileExtToRuntime = map[string]string{ - ".go": "go", - ".js": "node", - ".rb": "ruby", - ".py": "python", - ".rs": "rust", - ".cs": "dotnet", - ".fs": "dotnet", + ".go": "go", + ".js": "node", + ".rb": "ruby", + ".py": "python", + ".php": "php", + ".rs": "rust", + ".cs": "dotnet", + ".fs": "dotnet", } fnInitRuntimes []string @@ -99,7 +100,7 @@ func (a *initFnCmd) init(c *cli.Context) error { return err } if ff != nil { - return errors.New("function file already exists") + return errors.New("Function file already exists") } } @@ -130,7 +131,7 @@ func (a *initFnCmd) init(c *cli.Context) error { return err } - fmt.Println("func.yaml created.") + fmt.Println("func.yaml created") return nil } @@ -142,11 +143,11 @@ func (a *initFnCmd) buildFuncFile(c *cli.Context) error { a.name = c.Args().First() if a.name == "" || strings.Contains(a.name, ":") { - return errors.New("Please specify a name for your function in the following format /.\nTry: fn init /") + return errors.New("please specify a name for your function in the following format /.\nTry: fn init /") } if exists("Dockerfile") { - fmt.Println("Dockerfile found, will use that to build.") + fmt.Println("Dockerfile found. Let's use that to build...") return nil } @@ -157,9 +158,10 @@ func (a *initFnCmd) buildFuncFile(c *cli.Context) error { return err } a.runtime = rt - fmt.Printf("assuming %v runtime\n", rt) + fmt.Printf("Found %v, assuming %v runtime.\n", rt, rt) + } else { + fmt.Println("Runtime:", a.runtime) } - fmt.Println("runtime:", a.runtime) if _, ok := acceptableFnRuntimes[a.runtime]; !ok { return fmt.Errorf("init does not support the %s runtime, you'll have to create your own Dockerfile for this function", a.runtime) } @@ -193,5 +195,5 @@ func detectRuntime(path string) (runtime string, err error) { return runtime, nil } } - return "", fmt.Errorf("no supported files found to guess runtime, please set runtime explicitly with --runtime flag") + return "", fmt.Errorf("no supported files found to guess runtime, please set runtime explicitly with --runtime flag.") } diff --git a/fn/langs/base.go b/fn/langs/base.go index 606402f7b..1319ca014 100644 --- a/fn/langs/base.go +++ b/fn/langs/base.go @@ -11,6 +11,8 @@ func GetLangHelper(lang string) LangHelper { return &RubyLangHelper{} case "python": return &PythonHelper{} + case "php": + return &PhpLangHelper{} case "rust": return &RustLangHelper{} case "dotnet": diff --git a/fn/langs/php.go b/fn/langs/php.go new file mode 100644 index 000000000..e97dd5d6e --- /dev/null +++ b/fn/langs/php.go @@ -0,0 +1,49 @@ +package langs + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" +) + +type PhpLangHelper struct { + BaseHelper +} + +func (lh *PhpLangHelper) Entrypoint() string { + return "php func.php" +} + +func (lh *PhpLangHelper) HasPreBuild() bool { + return true +} + +func (lh *PhpLangHelper) PreBuild() error { + wd, err := os.Getwd() + if err != nil { + return err + } + + if !exists(filepath.Join(wd, "composer.json")) { + return nil + } + + pbcmd := fmt.Sprintf("docker run --rm -v %s:/worker -w /worker funcy/php:dev composer install", wd) + fmt.Println("Running prebuild command:", pbcmd) + parts := strings.Fields(pbcmd) + head := parts[0] + parts = parts[1:len(parts)] + cmd := exec.Command(head, parts...) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + if err := cmd.Run(); err != nil { + return fmt.Errorf("error running docker build: %v", err) + } + return nil +} + +func (lh *PhpLangHelper) AfterBuild() error { + return nil +} diff --git a/fn/main.go b/fn/main.go index d26c14a4f..a2821c75d 100644 --- a/fn/main.go +++ b/fn/main.go @@ -37,7 +37,7 @@ func newFn() *cli.App { app.Version = vers.Version app.Authors = []cli.Author{{Name: "Oracle Corporation"}} app.Description = "Oracle Functions command line tools" - app.UsageText = `Check the manual at https://github.com/treeder/functions/blob/master/fn/README.md` + app.UsageText = `Check the manual at https://gitlab.oracledx.com/odx/functions/blob/master/fn/README.md` cli.AppHelpTemplate = `{{.Name}} {{.Version}}{{if .Description}}