possible breakages: * `FN_HEADER` on cold are no longer `s/-/_/` -- this is so that cold functions can rebuild the headers as they were when they came in on the request (fdks, specifically), there's no guarantee that a reversal `s/_/-/` is the original header on the request. * app and route config no longer `s/-/_/` -- it seemed really weird to rewrite the users config vars on these. should just pass them exactly as is to env. * headers no longer contain the environment vars (previously, base config; app config, route config, `FN_PATH`, etc.), these are still available in the environment. this gets rid of a lot of the code around headers, specifically the stuff that shoved everything into headers when constructing a call to begin with. now we just store the headers separately and add a few things, like FN_CALL_ID to them, and build a separate 'config' now to store on the call. I thought 'config' was more aptly named, 'env' was confusing, though now 'config' is exactly what 'base_vars' was, which is only the things being put into the env. we weren't storing this field in the db, this doesn't break unless there are messages in a queue from another version, anyway, don't think we're there and don't expect any breakage for anybody with field name changes. this makes the configuration stuff pretty straight forward, there's just two separate buckets of things, and cold just needs to mash them together into the env, and otherwise hot containers just need to put 'config' in the env, and then hot format can shove 'headers' in however they'd like. this seems better than my last idea about making this easier but worse (RIP). this means: * headers no longer contain all vars, the set of base vars can only be found in the environment. * headers is only the headers from request + call_id, deadline, method, url * for cold, we simply add the headers to the environment, prepending `FN_HEADER_` to them, BUT NOT upper casing or `s/-/_/` * fixes issue where async hot functions would end up with `Fn_header_` prefixed headers * removes idea of 'base' vars and 'env'. this was a strange concept. now we just have 'config' which was base vars, and headers, which was base_env+headers; i.e. they are disjoint now. * casing for all headers will lean to be `My-Header` style, which should help with consistency. notable exceptions for cold only are FN_CALL_ID, FN_METHOD, and FN_REQUEST_URL -- this is simply to avoid breakage, in either hot format they appear as `Fn_call_id` still. * removes FN_PARAM stuff * updated doc with behavior weird things left: `Fn_call_id` e.g. isn't a correctly formatted http header, it should likely be `Fn-Call-Id` but I wanted to live to fight another day on this one, it would add some breakage. examples to be posted of each format below closes #329
4.2 KiB
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.
Also, for complete examples in various languages, see the examples directory.
Code
The most basic code layout in any language is as follows, this is pseudo code and is not meant to run.
# Read and parse from STDIN
body = JSON.parse(STDIN)
# Do something
return_struct = doSomething(body)
# If sync, respond:
STDOUT.write(JSON.generate(return_struct))
# If async, update something:
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. To read in the function body, just read from STDIN.
You will also have access to a set of environment variables, independent of the function's format:
FN_APP_NAME- the name of the application that matched this route, eg:myappFN_PATH- the matched route, eg:/helloFN_METHOD- the HTTP method for the request, eg:GETorPOSTFN_FORMAT- a string representing one of the function formats, currently eitherdefaultorhttp. Default isdefault.FN_MEMORY- a number representing the amount of memory available to the call, in MBFN_TYPE- the type for this call, currently 'sync' or 'async'
Dependent upon the function's format, additional variables that change on a per invocation basis will be in a certain location.
For default format, these will be in environment variables as well:
FN_DEADLINE- RFC3339 time stamp of the expiration (deadline) date of function execution.FN_REQUEST_URL- the full URL for the request (parsing example)FN_CALL_ID- a unique ID for each function execution.FN_METHOD- http method used to invoke this functionFN_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.$X- $X is the header that came in the http request that invoked this function.
For http format these will be in http headers:
Fn_deadline- RFC3339 time stamp of the expiration (deadline) date of function execution.Fn_request_url- the full URL for the request (parsing example)Fn_call_id- a unique ID for each function execution.Fn_method- the HTTP method used to invoke$X- the HTTP headers that were set for this request, exactly as they were sent in the request.
For json format, these will be fields in the json object (see
format):
call_idprotocol: { "headers": { "$X": [ "$Y" ] } }where$X:$Yis each http header exactly as it was sent in the request
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.
So to write output to logs, simply log to STDERR. Here are some examples in a few languages.
In Go, simply use the log package, it writes to STDERR by default.
log.Println("hi")
In Node.js:
console.error("hi");
More details for Node.js here.
In Ruby:
STDERR.puts("hi")
Using Lambda Functions
Lambda everywhere
Lambda support for Fn enables you to take your AWS Lambda functions and run them anywhere. You should be able to take your code and run them without any changes.
Creating Lambda functions is not much different than using regular functions, just use
the lambda-node runtime.
fn init --runtime lambda-node --name lambda-node
Be sure the filename for your main handler is func.js.
TODO: Make Java and Python use the new workflow too.