Docs on writing functions and the functions format. (#203)

* WIP on writing functions and the functions format.

* Added more docs on writing functions.

* Running on windows docs.

* Linked README to writing.md

* Minor fixes from PR feedback.
This commit is contained in:
Travis Reeder
2016-11-03 10:53:36 -07:00
committed by GitHub
parent 2c222fcd32
commit 4421c151c5
8 changed files with 325 additions and 49 deletions

View File

@@ -1,7 +1,8 @@
# IronFunctions Documentation
* [IronFunctions Options](api.md)
* [IronFunctions Run Options](options.md)
* [Writing Functions](function-format.md)
* [API Reference](https://app.swaggerhub.com/api/iron/functions/)
* [Running in Production](production.md)
* [Databases](databases/README.md)
@@ -12,3 +13,4 @@
* [Triggers](triggers.md)
* [Extending IronFunctions](extending.md)
* [Docker Configuration](docker.md)
* [Function Format](function-format.md)

108
docs/function-format.md Normal file
View File

@@ -0,0 +1,108 @@
# Open Function Format
This document will describe the details of how a function works, inputs/outputs, etc.
## Input
### STDIN and Environment Variables
While wanting to keep things simple, flexible and expandable, we decided to go back to the basics, using Unix input and output. Standard in is easy to use in any
language and doesn't require anything extra. It also allows streaming input so we can do things like keeping a container running some time and stream
requests into the container.
Configuration values, environment information and other things will be passed in through environment variables.
### Input Formats
The goals of the input format are the following:
* Very easy to use and parse
* Streamable for increasing performance (more than one call per container execution)
* Ability to build higher level abstractions on top (ie: Lambda syntax compatible)
The format is still up for discussion and in order to move forward and remain flexible, it's likely we will just allow different input formats and the
function creator can decide what they want, on a per function basis. Default being the simplest format to use.
#### Default Input Format
The default input format is simply the request body itself plus some environment variables. For instance, if someone were to post a JSON body, the unmodified body would
be sent in via STDIN.
Pros:
* Very simple to use
Cons:
* Not streamable
#### HTTP/1 Input Format (Not implemented)
`--input-format http`
HTTP format could be a good option as it is in very common use obviously, most languages have some semi-easy way to parse it, and it's streamable. The basic format
is:
```
REQUEST LINE
HEADER
BLANK LINE
BODY
```
The header keys and values would be populated with information about the function call such as the request URL and query parameters.
Body length is determined by the [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.3) header, which is mandatory.
Pros:
* Streamable
* Common format
Cons:
* Requires a parsing library or fair amount of code to parse headers properly
* Double parsing - headers + body (if body is to be parsed, such as json)
#### JSON/HTTP Input Format (Not implemented)
`--input-format json-http`
The idea here is to keep the HTTP base structure, but make it a bit easier to parse by making the `request line` and `headers` a JSON struct.
Eg:
```
{
"request_url":"http://....",
"params": {
"blog_name": "yeezy"
}
}
BLANK LINE
BODY
```
Pros:
* Streamable
* Easy to parse headers
Cons:
* New, unknown format
## Output
### STDOUT
For synchronous: True to form, whatever is written to standard out is returned as the response. If you want to return some JSON output, just write it directly to STDOUT.
TODO: How to change response headers? Perhaps a similar style as input? Headers, then body. Default headers can be defined on the route and overridden on output.
For asynchronous: STDOUT will be written to /dev/null until [further notice](https://github.com/iron-io/functions/issues/173). We do not want to write this
to the logs now, then change it later, otherwise people will start to depend on it.
### STDERR
Standard error is reserved for logging, like it was meant to be. Anything you output to STDERR will show up in the logs. And if you use a log
collector like logspout, you can collect those logs in a central location. See [logging](logging.md).

View File

@@ -1,7 +1,4 @@
## IronFunctions Config Options
# IronFunctions Configuration Options
When starting IronFunctions, you can pass in the following configuration variables as environment variables. Use `-e VAR_NAME=VALUE` in
docker run. For example:
@@ -39,5 +36,4 @@ docker run -e VAR_NAME=VALUE ...
<td>LOG_LEVEL</td>
<td>Set to `DEBUG` to enable debugging. Default is INFO.</td>
</tr>
</table>

9
docs/windows.md Normal file
View File

@@ -0,0 +1,9 @@
# Running on Windows
Windows doesn't support Docker in Docker so you'll change the run command to the following:
```sh
docker run --rm --name functions -it -v /var/run/docker.sock:/var/run/docker.sock -v $PWD/data:/app/data -p 8080:8080 iron/functions
```
Then everything should work as normal.

81
docs/writing.md Normal file
View File

@@ -0,0 +1,81 @@
# Writing Functions
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.md).
## Code
The most basic code layout in any language is as follows, this is pseudo code and is not meant to run.
```ruby
# Read and parse from STDIN
body = JSON.parse(STDIN)
# Do something
return_struct = doSomething(body)
# Respond if sync:
STDOUT.write(JSON.generate(return_struct))
# or update something if async
db.update(return_struct)
```
## Inputs
Inputs are provided through standard input and environment variables. We'll just talk about the default input format here, but you can find others [here](function-format.md).
To read in the function body, just read from STDIN.
You will also have access to a set of environment variables.
* REQUEST_URL - the full URL for the request
* ROUTE - the matched route
* METHOD - the HTTP method for the request
* CONFIG_X - any configuration values you've set for the Application or the Route. Replace X with the upper cased name of the config variable you set.
* HEADER_X - the HTTP headers that were set for this request. Replace X with the upper cased name of the header and replace dashes in the header with underscores.
Warning: these may change before release.
## Logging
Standard out is where you should write response data for synchronous functions. Standard error
is where you should write for logging, as [it was intended](http://www.jstorimer.com/blogs/workingwithcode/7766119-when-to-use-stderr-instead-of-stdout).
So to write output to logs, simply log to STDERR. Here are some examples in a few languages.
In Go, simply use the [log](https://golang.org/pkg/log/) package, it writes to STDERR by default.
```go
log.Println("hi")
```
In Node.js:
```node
console.error("hi");
```
[More details for Node.js here](http://stackoverflow.com/a/27576486/105562).
In Ruby:
```ruby
STDERR.puts("hi")
```
## Packaging
Packaging a function is essentially just creating a Docker image for your function with an ENTRYPOINT.
The basic Dockerfile for most languages is something like this:
```
# Choose base image
FROM iron/go
# Set th working directory
WORKDIR /function
# Add your binary or code to the working directory
ADD hello /function/
# Set what will run when a container is started for this image
ENTRYPOINT ["./hello"]
```