diff --git a/Dockerfile b/Dockerfile index f5439d187..335f8ecc9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM iron/dind -RUN mkdir /app -ADD functions-alpine /app/functions WORKDIR /app +ADD functions-alpine /app/functions + CMD ["./functions"] diff --git a/README.md b/README.md index 3b2b4b402..d4bac67e3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# IronFunctions +![IronFunctions](docs/assets/logo-black-400w.png) [![CircleCI](https://circleci.com/gh/iron-io/functions.svg?style=svg)](https://circleci.com/gh/iron-io/functions) [![GoDoc](https://godoc.org/github.com/iron-io/functions?status.svg)](https://godoc.org/github.com/iron-io/functions) @@ -8,7 +8,7 @@ Welcome to IronFunctions! The open source serverless platform. ## What is IronFunctions? IronFunctions is an open source serverless platform, or as we like to refer to it, Functions as a -Service (FaaS) platform that you can run anywhere. +Service (FaaS) platform that you can run anywhere. * [Run anywhere](docs/faq.md#where-can-i-run-ironfunctions) * Public cloud, hybrid, on-premise @@ -17,6 +17,8 @@ Service (FaaS) platform that you can run anywhere. * [AWS Lambda support](docs/lambda/README.md) * Easy to use * Easy to scale +* Written in Go +* API Gateway built in ## What is Serverless/FaaS? @@ -55,7 +57,7 @@ and you'll find plenty of information. We have pretty thorough post on the Iron. ## Join Our Community -First off, join the community! +Join our Slack community to get help and give feedback. [![Slack Status](https://open-iron.herokuapp.com/badge.svg)](http://get.iron.io/open-slack) @@ -63,24 +65,77 @@ First off, join the community! This guide will get you up and running in a few minutes. -### Run IronFunctions Container +### Prequisites -To get started quickly with IronFunctions, you can just fire up an `iron/functions` container: +* Docker 1.10 or later installed and running +* Logged into Docker Hub (`docker login`) + +### Run IronFunctions + +To get started quickly with IronFunctions, just fire up an `iron/functions` container: ```sh docker run --rm -it --name functions --privileged -v $PWD/data:/app/data -p 8080:8080 iron/functions ``` -**Note**: A list of configurations via env variables can be found [here](docs/options.md). +This will start IronFunctions in single server mode, using an embedded database and message queue. You can find all the +configuration options [here](docs/options.md). If you are on Windows, see [windows](docs/operating/windows.md). ### CLI tool -The IronFunctions CLI tool is optional, but it makes things easier. Install it with: +Install the IronFunctions CLI tool: ```sh curl -sSL http://get.iron.io/fnctl | sh ``` +### Write a 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. + +The following is a Go function that just returns "Hello ${NAME}!": + +```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) +} +``` + +Copy and paste the code above into a file called `hello.go`, then run: + +```sh +# create func.yaml file, replace $USERNAME with your Docker Hub username. +fnctl init $USERNAME/hello +# build the function +fnctl build +# test it +fnctl run +# push it to Docker Hub +fnctl push +# create an app +fnctl apps create myapp +# create a route that maps /hello to your new function +fnctl routes create myapp /hello +``` + +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). + ### Create an Application An application is essentially a grouping of functions, that put together, form an API. Here's how to create an app. @@ -99,7 +154,7 @@ curl -H "Content-Type: application/json" -X POST -d '{ [More on apps](docs/apps.md). -Now that we have an app, we can map routes to functions. +Now that we have an app, we can route endpoints to functions. ### Add a Route @@ -209,13 +264,27 @@ Read more on [logging](docs/logging.md). See [Writing Functions](docs/writing.md). +And you can find a bunch of examples in the [/examples](/examples) directory. + ## More Documentation See [docs/](docs/README.md) for full documentation. -## Want to contribute to IronFunctions? +## Roadmap -See [contributing](CONTRIBUTING.md). +These are the high level roadmap goals. See [milestones](https://github.com/iron-io/functions/milestones) for detailed issues. + +* Alpha 1 - November 2016 + * Initial release of base framework + * Lambda support +* Alpha 2 - December 2016 + * Streaming input for hot containers #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 @@ -224,4 +293,8 @@ You can get community support via: * [Stack Overflow](http://stackoverflow.com/questions/tagged/ironfunctions) * [Slack](https://get.iron.io/open-slack) -You can get commercial support by contacting [Iron.io](https://iron.io) +You can get commercial support by contacting [Iron.io](https://iron.io/contact) + +## Want to contribute to IronFunctions? + +See [contributing](CONTRIBUTING.md). diff --git a/api/ifaces/app.go b/api/ifaces/app.go index d70c2b3f0..f2a9bffb0 100644 --- a/api/ifaces/app.go +++ b/api/ifaces/app.go @@ -9,7 +9,6 @@ type App interface { } type Route interface { - // AppName() string `json:"appname"` Path() string Image() string Headers() http.Header diff --git a/build.ps1 b/build.ps1 index eee32f174..c12014be8 100644 --- a/build.ps1 +++ b/build.ps1 @@ -11,7 +11,7 @@ function build () { function run () { build - docker run --rm --privileged -it -e LOG_LEVEL=debug -e "DB_URL=bolt:///app/data/bolt.db" -v ${pwd}/data:/app/data -p 8080:8080 iron/functions + docker run --rm --name functions -it -v /var/run/docker.sock:/var/run/docker.sock -e LOG_LEVEL=debug -e "DB_URL=bolt:///app/data/bolt.db" -v $PWD/data:/app/data -p 8080:8080 iron/functions } switch ($cmd) diff --git a/docs/README.md b/docs/README.md index 13d924221..f302a77ac 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,6 +10,7 @@ If you are a developer using IronFunctions through the API, this section is for * [fnctl (CLI Tool)](/fnctl/README.md) * [Writing functions](writing.md) * [Writing Lambda functions](docs/lambda/create.md) +* [Function file (func.yaml)](docs/function-file.md) * [Packaging functions](packaging.md) * [Open Function Format](function-format.md) * [API Reference](https://app.swaggerhub.com/api/iron/functions/) diff --git a/docs/assets/index.html b/docs/assets/index.html new file mode 100644 index 000000000..0fa5f80f5 --- /dev/null +++ b/docs/assets/index.html @@ -0,0 +1,3 @@ + + + diff --git a/docs/assets/logo-black-400w.png b/docs/assets/logo-black-400w.png new file mode 100644 index 000000000..a836bf5d4 Binary files /dev/null and b/docs/assets/logo-black-400w.png differ diff --git a/docs/assets/logo-black-800w.png b/docs/assets/logo-black-800w.png new file mode 100644 index 000000000..1178ac2c4 Binary files /dev/null and b/docs/assets/logo-black-800w.png differ diff --git a/docs/faq.md b/docs/faq.md index fa7f08513..71a9babc8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -10,9 +10,9 @@ base [function format](function-format.md). Anywhere. Any cloud, on-premise, on your laptop. As long as you can run a Docker container, you can run IronFunctions. -## Which orchestration platforms does IronFunctions support? +## Which orchestration tools does IronFunctions support? -IronFunctions can run using any orchestration tool, any server +IronFunctions can be deployed using any orchestration tool. ## Does IronFunctions require Docker? diff --git a/docs/function-file.md b/docs/function-file.md new file mode 100644 index 000000000..357d3049e --- /dev/null +++ b/docs/function-file.md @@ -0,0 +1,53 @@ +# Function files + +Functions files are used to assist fnctl to help you when creating functions. + +The files can be named as: + +- func.yaml +- func.json + +An example of a function file: + +```yaml +name: iron/hello +version: 0.0.1 +type: sync +memory: 128 +config: + key: value + key2: value2 + keyN: valueN +build: +- make +- make test +``` + +`app` (optional) is the application name to which this function will be pushed +to. + +`image` is the name and tag to which this function will be pushed to and the +route updated to use it. + +`route` (optional) allows you to overwrite the calculated route from the path +position. You may use it to override the calculated route. + +`version` represents current version of the function. When publishing, it is +appended to the image as a tag. + +`type` (optional) allows you to set the type of the route. `sync`, for functions +whose response are sent back to the requester; or `async`, for functions that +are started and return a task ID to customer while it executes in background. +Default: `sync`. + +`memory` (optional) allows you to set a maximum memory threshold for this +function. If this function exceeds this limit during execution, it is stopped +and error message is logged. Default: `128`. + +`config` (optional) is a set of configurations to be passed onto the route +setup. These configuration options shall override application configuration +during functions execution. + +`build` (optional) is an array of shell calls which are used to helping building +the image. These calls are executed before `fnctl` calls `docker build` and +`docker push`. \ No newline at end of file diff --git a/docs/operating/production.md b/docs/operating/production.md index 0303524ca..05e08914a 100644 --- a/docs/operating/production.md +++ b/docs/operating/production.md @@ -9,7 +9,7 @@ to use more production ready components. Here's a rough diagram of what a production deployment looks like: -![IronFunctions Architecture Diagram](/docs/assets/architecture.svg) +![IronFunctions Architecture Diagram](../assets/architecture.png) ## Load Balancer diff --git a/docs/packaging.md b/docs/packaging.md index 1d66175a2..940af46bf 100644 --- a/docs/packaging.md +++ b/docs/packaging.md @@ -7,7 +7,17 @@ Packaging a function has two parts: Once it's pushed to a registry, you can use it by referencing it when adding a route. -## Creating an image +## Using fnctl + +This is the easiest way to build, package and publish your functions. + + + + + +## + +### Creating an image The basic Dockerfile for most languages is along these lines: @@ -35,7 +45,7 @@ Or using [fnctl](../fnctl/README.md): fnctl build ``` -## Push your image +### Push your image This part is simple: diff --git a/docs/writing.md b/docs/writing.md index 494fe4ca7..6a3ab5916 100644 --- a/docs/writing.md +++ b/docs/writing.md @@ -3,6 +3,8 @@ This will give you the basic overview of writing base level functions. You can also use higher level abstractions that make it easier such as [lambda](lambda/README.md). +Also, for complete examples in various languages, see the [examples directory](/examples). + ## Code The most basic code layout in any language is as follows, this is pseudo code and is not meant to run. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 000000000..3b6ea228f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,9 @@ +# 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. + +## Hello World Examples + +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. diff --git a/examples/hello/go/.gitignore b/examples/hello/go/.gitignore index c7428d9d4..47868d339 100644 --- a/examples/hello/go/.gitignore +++ b/examples/hello/go/.gitignore @@ -4,4 +4,4 @@ vendor/ /app /__uberscript__ -function.yaml +func.yaml diff --git a/examples/hello/go/README.md b/examples/hello/go/README.md index 166d8970e..02bc28e70 100644 --- a/examples/hello/go/README.md +++ b/examples/hello/go/README.md @@ -3,22 +3,20 @@ This example will show you how to test and deploy Go (Golang) code to IronFunctions. ```sh +# create your func.yaml file fnctl init /hello +# build the function fnctl build # test it cat hello.payload.json | fnctl run # push it to Docker Hub fnctl push # Create a route to this function on IronFunctions -fnctl routes create myapp /hello YOUR_DOCKERHUB_USERNAME/hello:0.0.X -# todo: Image name could be optional if we read the function file for creating the route. Then command could be: fnctl routes create myapp /hello ``` -Now you use your function on IronFunctions: +Now you can call your function on IronFunctions: ```sh curl -H "Content-Type: application/json" -X POST -d @hello.payload.json http://localhost:8080/r/myapp/hello ``` - -Or surf to it: http://localhost:8080/r/myapp/hello diff --git a/examples/hello/go/function.yaml b/examples/hello/go/function.yaml new file mode 100644 index 000000000..946aea6a2 --- /dev/null +++ b/examples/hello/go/function.yaml @@ -0,0 +1,4 @@ +name: treeder/hello4 +version: 0.0.1 +runtime: go +entrypoint: ./func diff --git a/examples/hello/go/hello.go b/examples/hello/go/hello.go index fa1ef4b03..bf0518407 100644 --- a/examples/hello/go/hello.go +++ b/examples/hello/go/hello.go @@ -13,5 +13,5 @@ type Person struct { func main() { p := &Person{Name: "World"} json.NewDecoder(os.Stdin).Decode(p) - fmt.Println("Hello", p.Name, "!!!") + fmt.Printf("Hello %v!", p.Name) } diff --git a/examples/hello/node/.gitignore b/examples/hello/node/.gitignore index 6142b82f2..8977e475d 100644 --- a/examples/hello/node/.gitignore +++ b/examples/hello/node/.gitignore @@ -1,3 +1,3 @@ node_modules/ -function.yaml +func.yaml Dockerfile diff --git a/examples/hello/node/README.md b/examples/hello/node/README.md index c5585083e..c46e3b67c 100644 --- a/examples/hello/node/README.md +++ b/examples/hello/node/README.md @@ -3,15 +3,15 @@ This example will show you how to test and deploy a Node function to IronFunctions. ```sh +# create your func.yaml file fnctl init /hello +# build the function fnctl build # test it cat hello.payload.json | fnctl run # push it to Docker Hub for use with IronFunctions fnctl push # Create a route to this function on IronFunctions -fnctl routes create myapp /hello YOUR_DOCKERHUB_USERNAME/hello:0.0.X -# todo: Image name could be optional if we read the function file for creating the route. Then command could be: fnctl routes create myapp /hello ``` diff --git a/examples/hello/node/function.yaml b/examples/hello/node/function.yaml new file mode 100644 index 000000000..e08c8786a --- /dev/null +++ b/examples/hello/node/function.yaml @@ -0,0 +1,4 @@ +name: treeder/node4 +version: 0.0.2 +runtime: node +entrypoint: node hello.js diff --git a/examples/hello/php/README.md b/examples/hello/php/README.md index 204d2ab17..240723da7 100644 --- a/examples/hello/php/README.md +++ b/examples/hello/php/README.md @@ -2,13 +2,14 @@ This example will show you how to test and deploy Go (Golang) code to IronFunctions. -### 1. Prepare the `functions.yaml` file: +### 1. Prepare the `func.yaml` file: -At functions.yaml you will find: +At func.yaml you will find: ```yml app: phpapp route: /hello -image: USERNAME/hello:0.0.1 +image: USERNAME/hello +version: 0.0.1 build: - docker run --rm -v "$PWD":/worker -w /worker iron/php:dev composer install ``` diff --git a/examples/hello/python/README.md b/examples/hello/python/README.md index debc4f523..8328c524c 100644 --- a/examples/hello/python/README.md +++ b/examples/hello/python/README.md @@ -2,13 +2,15 @@ This example will show you how to test and deploy Go (Golang) code to IronFunctions. -### 1. Prepare the `functions.yaml` file: +### 1. Prepare the `func.yaml` file: + +At func.yaml you will find: -At functions.yaml you will find: ```yml app: pythonapp route: /hello -image: USERNAME/hello:0.0.1 +image: USERNAME/hello +version: 0.0.1 build: - docker run --rm -v "$PWD":/worker -w /worker iron/python:2-dev pip install -t packages -r requirements.txt ``` diff --git a/fnctl/README.md b/fnctl/README.md index 9db261a68..453cc0eb7 100644 --- a/fnctl/README.md +++ b/fnctl/README.md @@ -1,16 +1,49 @@ # IronFunctions CLI -## Init +## Creating Functions -usage: fnctl init [--runtime node] [--entrypoint "node hello.js"] +### init -Init will help you create a function.yaml file for the current directory. +Init will help you create a [function file](../docs/function-file.md) (func.yaml) in the current directory. -If there's a Dockerfile found, this will generate the basic file with just the image name. -It will then try to decipher the runtime based on the files in the current directory, if it can't figure it out, it will ask. -It will then take a best guess for what the entrypoint will be based on the language, it it can't guess, it will ask. +```sh +fnctl init [--runtime node] [--entrypoint "node hello.js"] +``` -## Basic +`--runtime` and `--entrypoint` are optional, init will try to figure out it out based on the files in the current directory. +If it can't figure it out, it will tell you. + +If there's a Dockerfile found, it will use that as is + +### Build, Bump, Run, Push + +`fnctl` provides a few commands you'll use while creating and updating your functions: `build`, `bump`, `run` and `push`. + +Build will build the image for your function. + +```sh +fnctl build +``` + +Bump will bump the version number in your func.yaml file. Versions must be in [semver](http://semver.org/) format. + +```sh +fnctl bump +``` + +Run will help you test your function. Functions read input from STDIN, so you can pipe the payload into the function like this: + +```sh +cat `payload.json` | fnctl run +``` + +Push will push the function image to Docker Hub. + +```sh +fnctl push +``` + +## Using the API You can operate IronFunctions from the command line. @@ -40,6 +73,47 @@ $ fnctl routes delete otherapp hello # delete route /hello deleted ``` +## Application level configuration + +When creating an application, you can configure it to tweak its behavior and its +routes' with an appropriate flag, `config`. + +Thus a more complete example of an application creation will look like: +```sh +fnctl apps create --config DB_URL=http://example.org/ otherapp +``` + +`--config` is a map of values passed to the route runtime in the form of +environment variables prefixed with `CONFIG_`. + +Repeated calls to `fnctl apps create` will trigger an update of the given +route, thus you will be able to change any of these attributes later in time +if necessary. + +## Route level configuration + +When creating a route, you can configure it to tweak its behavior, the possible +choices are: `memory`, `type` and `config`. + +Thus a more complete example of route creation will look like: +```sh +fnctl routes create --memory 256 --type async --config DB_URL=http://example.org/ otherapp /hello iron/hello +``` + +`--memory` is number of usable MiB for this function. If during the execution it +exceeds this maximum threshold, it will halt and return an error in the logs. + +`--type` is the type of the function. Either `sync`, in which the client waits +until the request is successfully completed, or `async`, in which the clients +dispatches a new request, gets a task ID back and closes the HTTP connection. + +`--config` is a map of values passed to the route runtime in the form of +environment variables prefixed with `CONFIG_`. + +Repeated calls to `fnctl route create` will trigger an update of the given +route, thus you will be able to change any of these attributes later in time +if necessary. + ## Changing target host `fnctl` is configured by default to talk http://localhost:8080. @@ -101,142 +175,11 @@ It means that first subdirectory are always considered app names (e.g. `myapp` and `other`), each subdirectory of these firsts are considered part of the route (e.g. `route1/subroute1`). -`fnctl update` expects that each directory to contain a file `functions.yaml` +`fnctl update` expects that each directory to contain a file `func.yaml` which instructs `fnctl` on how to act with that particular update, and a Dockerfile which it is going to use to build the image and push to Docker Hub. -## Functions files (functions.yaml) - -Functions files are used to assist fnctl to execute bulk updates of your -functions. The files can be named as: - -- functions.yaml -- functions.yml -- function.yaml -- function.yml -- functions.json -- function.json -- fn.yaml -- fn.yml -- fn.json - -An example of a function file: -```yaml -app: myapp -name: iron/hello -route: "/custom/route" -version: 0.0.1 -type: sync -memory: 128 -config: - key: value - key2: value2 - keyN: valueN -build: -- make -- make test -``` - -`app` (optional) is the application name to which this function will be pushed -to. - -`image` is the name and tag to which this function will be pushed to and the -route updated to use it. - -`route` (optional) allows you to overwrite the calculated route from the path -position. You may use it to override the calculated route. - -`version` represents current version of the function. When publishing, it is -appended to the image as a tag. - -`type` (optional) allows you to set the type of the route. `sync`, for functions -whose response are sent back to the requester; or `async`, for functions that -are started and return a task ID to customer while it executes in background. -Default: `sync`. - -`memory` (optional) allows you to set a maximum memory threshold for this -function. If this function exceeds this limit during execution, it is stopped -and error message is logged. Default: `128`. - -`config` (optional) is a set of configurations to be passed onto the route -setup. These configuration options shall override application configuration -during functions execution. - -`build` (optional) is an array of shell calls which are used to helping building -the image. These calls are executed before `fnctl` calls `docker build` and -`docker push`. - -## Build, Bump, Push - -When dealing with a lot of functions you might find yourself making lots of -individual calls. `fnctl` offers two command to help you with that: `build` and -`bump`. - -```sh -$ fnctl build -path result -/app/hello done -/app/test done -``` - -`fnctl build` is similar to `publish` except it neither publishes the resulting -docker image to Docker Hub nor updates the routes in IronFunctions server. - -```sh -$ fnctl bump -path result -/app/hello done -/app/test done -``` - -`fnctl bump` will scan all IronFunctions whose `version` key in function file -follows [semver](http://semver.org/) rules and bump their version according. - -`fnctl push` will scan all IronFunctions and push their images to Docker Hub, -and update their routes accordingly. - -## Application level configuration - -When creating an application, you can configure it to tweak its behavior and its -routes' with an appropriate flag, `config`. - -Thus a more complete example of an application creation will look like: -```sh -fnctl apps create --config DB_URL=http://example.org/ otherapp -``` - -`--config` is a map of values passed to the route runtime in the form of -environment variables prefixed with `CONFIG_`. - -Repeated calls to `fnctl apps create` will trigger an update of the given -route, thus you will be able to change any of these attributes later in time -if necessary. - -## Route level configuration - -When creating a route, you can configure it to tweak its behavior, the possible -choices are: `memory`, `type` and `config`. - -Thus a more complete example of route creation will look like: -```sh -fnctl routes create --memory 256 --type async --config DB_URL=http://example.org/ otherapp /hello iron/hello -``` - -`--memory` is number of usable MiB for this function. If during the execution it -exceeds this maximum threshold, it will halt and return an error in the logs. - -`--type` is the type of the function. Either `sync`, in which the client waits -until the request is successfully completed, or `async`, in which the clients -dispatches a new request, gets a task ID back and closes the HTTP connection. - -`--config` is a map of values passed to the route runtime in the form of -environment variables prefixed with `CONFIG_`. - -Repeated calls to `fnctl route create` will trigger an update of the given -route, thus you will be able to change any of these attributes later in time -if necessary. - -## Build +## Contributing Ensure you have Go configured and installed in your environment. Once it is done, run: diff --git a/fnctl/errors.go b/fnctl/errors.go index daa6764cc..ab9072d5a 100644 --- a/fnctl/errors.go +++ b/fnctl/errors.go @@ -1,13 +1,13 @@ package main -type NotFoundError struct { +type notFoundError struct { S string } -func (e *NotFoundError) Error() string { +func (e *notFoundError) Error() string { return e.S } -func newNotFoundError(s string) *NotFoundError { - return &NotFoundError{S: s} +func newNotFoundError(s string) *notFoundError { + return ¬FoundError{S: s} } diff --git a/fnctl/init.go b/fnctl/init.go index c78a3f76e..8eb14892c 100644 --- a/fnctl/init.go +++ b/fnctl/init.go @@ -75,7 +75,7 @@ func initFn() cli.Command { func (a *initFnCmd) init(c *cli.Context) error { if !a.force { ff, err := findFuncfile() - if _, ok := err.(*NotFoundError); !ok && err != nil { + if _, ok := err.(*notFoundError); !ok && err != nil { return err } if ff != nil { diff --git a/fnctl/run.go b/fnctl/run.go index fac6ca328..04b8716bf 100644 --- a/fnctl/run.go +++ b/fnctl/run.go @@ -38,7 +38,7 @@ func (r *runCmd) run(c *cli.Context) error { if image == "" { ff, err := findFuncfile() if err != nil { - if _, ok := err.(*NotFoundError); ok { + if _, ok := err.(*notFoundError); ok { return errors.New("error: image name is missing or no function file found") } else { return err