OVH Guide

OVH Functions

OVH Functions is a FaaS (Function as a Service) platform that allows you to focus only on code.

You write functions code as you're used to, and deploy them with a single command. It's as simple as that.

OVH Functions manages the execution environment and the scalability of your functions.

The platform provides you logs and metrics. You can inject environment variables in your functions.

Your functions can be invoked with HTTP requests, Kafka messages or Scheduled events using triggers.

Quickstart

Deploy your first function in seconds.

Summary

Writing OVH Functions

OVH Functions can be written in JavaScript (Node.js), Python, Perl or Go (see runtimes).

JavaScript (Node.js)

Your function's source code must be exported in a Node.js module file .js and takes 2 parameters:

  • event: which holds the data associated with the event that triggered the execution of the function.
  • callback: which you must use to signal completion of the function. It's a function which interprets the first argument as an error and the second argument as the result.

The body of the HTTP request or the Kafka message is available in event.data. It's an object if it's a valid json, else a string.

exports.hello = function(event, callback) {
  console.log("event", event);

  var name = event.data;
  if ( name == "" ) {
    name = "World"
  }

  callback(null, "Hello " + name + " from OVH Functions!")
}

Dependencies

In this environment, you can install npm dependencies (http://npmjs.com/) thanks to the package.json file in the current folder. You can also use alpine packages requirements.

Python

In Python your function's source code must be present in a .py file and takes one parameter:

  • event: dictionary which holds the data associated with the event that triggered the execution of the function. The payload is in a data property of the event.

The body of the HTTP request or the Kafka message is available in event['data']. It's a dict if it's a valid json, else a string.

To finish properly the execution of your function, use a "return" statement with the result of your function. If you need to raise an error, use the 'raise Exception()' statement.

def hello(event):
  print(event)

  name = event['data']
  if name == "":
    name = "World"

  return "Hello " + name + " from OVH Functions!"

Dependencies

In this environment, you can install pip dependencies (https://pypi.python.org) thanks to the requirements.txt file in the current folder. You can also use alpine packages requirements.

Perl

Using Perl your function's source code must be present in a .pm file and takes one parameter:

  • event: reference which holds the data associated with the event that triggered the execution of the function. The payload is in the 'data' hash referred by event.

The body of the HTTP request or the Kafka message is available in $event->{'data'}. It's a hash if it's a valid json, else a string.

To finish properly the execution of your function, use a "return" statement with the result of your function. If you need to raise an error, use the 'die' statement.

sub hello {
  my ($event) = @_;
  use Data::Dumper;
  print Dumper(\$event);

  my $name = $event->{'data'};
  if( $name eq "" ) {
    $name = "World";
  } 

  return "Hello $name from OVH Functions!";
}
1;

Dependencies

You can use generic alpine packages requirements.

Go

With Go, your function's source code must be present in a .go file and takes one parameter:

  • event: struct of type Event which holds the data associated with the event that triggered the execution of the function.
  type Event struct {
    Data   string
    Params map[string]string
  }

The body of the HTTP request or the Kafka message is available in the event.Data string. While the query parameters of the HTTP request are available in the event.Params map.

To finish the execution of your function, use a "return" statement containing the result of your function as a string and an error if needed.

package main

import (
  "fmt"

  "github.com/ovhlabs/functions/go-sdk/event"
)

func Hello(event event.Event) (string, error) {
  fmt.Println(event)

  name := event.Data
  if name == "" {
    name = "World"
  }

  return "Hello " + name + " from OVH Functions!", nil
}

Dependencies

A Go function can import every go packages publicly available. The dependencies are downloaded during the build of the function. If you need to use a private package, vendor it.

Bash

In Bash your function's source code must be present in a .sh file and takes one parameter ($1):

  • event: The body of the HTTP request or the Kafka message in JSON which triggered the execution of the function.

You can use stderr if you want to log something (>&2 echo).

To finish the execution of your function, use a "return" statement to indicate a success (return 0) or an error (return 1) and stdout (echo) to specify the result of your function.

function hello() {
  >&2 echo "$1"

  declare name=$1
  if [ "$name" == "" ]; then
    name="World"
  fi

  echo "Hello $name from OVH Functions!"
  return 0
}

Dependencies

You can use generic alpine packages requirements.

Deploying OVH Functions

The configuration of your functions is described in a functions.yml file in YAML format.

Each function to deploy is described by:

  • a name
  • a runtime
  • a handler composed of:
    • the module file name without the extension
    • the exported function name
functions:
  hello:
    runtime: nodejs
    handler: example.hello

and optionally environment variables:

functions:
  hello:
    runtime: nodejs
    handler: example.hello
    environment:
      ENV: staging
      REDIS_URL: ${env:REDIS_URL}

and triggers:

functions:
  hello:
    runtime: nodejs
    handler: example.hello
    events:
      - kafka:
          topic: thbkrkr.staging.pong

Then you can use the following command to deploy your function:

ovh-functions deploy

Memory allocation

By default the memory allocated to a function is 64MB. If you want to change it, you need to add a 'memory' parameter in the functions.yml file:

functions:
  hello:
    runtime: nodejs
    handler: example.hello
    memorySize: 128

Memory is expressed in Megabyte (MiB) and must be between 32 and 512.

Timeout

By default the functions are not allowed to run for more than 30 seconds. If you want to change it, you need to add a 'timeout' parameter in the functions.yml file:

functions:
  hello:
    runtime: nodejs
    handler: example.hello
    timeout: 45

Timeout is expressed in seconds and must be between 1 and 300 (5min).

File Upload

Directories and files stored in the current directory will be sent to the API and will be available during the function execution.

If you do NOT want to upload some files. You can list these in a .functionsignore file in the current folder. This file works the same way as the famous .gitignore.

#.functionsignore
.git*
build/

Alpine package

You can install any alpine packages with the apk.list file.

Example:

# Install certificates authorities
ca-certificates
mailcap
...

Environment variables

To use environment variables in your functions, you first need to declare them in the functions.yml config file under the keyword 'environment'.

You can either declare static environment variables or use the ${env:VAR_1} syntax where VAR_1 is the name of a variable already exported in your current local environment:

# functions.yml
functions:
  env:
    runtime: nodejs
    handler: example.env
    environment:
      VAR_1: ${env:VAR_1}
      VAR_2: something

Then, to access your environment variables in your functions, you can use the standard way.

Using Node.js, you can access it reading the process.env property:

exports.env = function(event, callback) {
  callback(null, "I'm printing VAR_1: " + process.env.VAR_1 + " and VAR_2: " + process.env.VAR_2)
}

In Python, use os.environ['VAR']:

def env(event):
  return "I'm printing VAR_1: " + os.environ['VAR_1'] + " and VAR_2: " + os.environ['VAR_2']

In Perl, use $ENV{'VAR'}:

sub env {
  return "I'm printing VAR_1: " . $ENV{'VAR_1'}  . " and VAR_2: " . $ENV{'VAR_2'};
}

And in Go, use os.Getenv("VAR"}:

func Env(event event.Event) (string, error) {
  return "I'm printing VAR_1: " + os.Getenv("VAR_1") + " and VAR_2: " + os.Getenv("VAR_2"), nil
}

Triggers

HTTP

The HTTP trigger is available by default for each function. You can retrieve the URL using the describe command.

The request can be of any type: GET, POST, PUT, DELETE... The HTTP request body, query parameters and method are available in the 'event' object of your functions.

curl -s -XPOST https://api.staging.functions.ovh/f/1234567890/ping?token=AZERTYUIOP&z=3 -d '{
  "message": "pong"
}'

Request method

The HTTP request method (GET, POST, PUT, DELETE...) used to call the HTTP endpoint is available in event.method (JS), event['method'] (Python), event->{'method'} (Perl) or event.Method (Go).

Query parameters

All query parameters defined using the HTTP trigger are available in event.params (JS), event['params'] (Python), event->{'params'} (Perl) or event.Params (Go).

Kafka

Your functions can be invoked for each Kafka message produced on a given OVH Queue topic.

To enable the Kafka trigger, configure a topic in the functions.yml configuration file:

# functions.yml
functions:
  notify:
    handler: alerter.notify
    events:
      - kafka:
          topic: mon.events

By default, the topic must belong to you. However, you can provide credentials for OVH Queue topics that are not your property:

# functions.yml
functions:
  notify:
    handler: alerter.notify
    events:
      - kafka:
          topic: mon.events
          region: SBG1
          username: mon.user
          password: secretpassword

Schedule

Your can schedule the execution of your functions using 2 different syntax:

  • Cron:
# functions.yml
functions:
  clock:
    handler: example.clock
    events:
      - schedule:
          rate: cron(* * * * *)
  • Rate:
# functions.yml
functions:
  clock:
    handler: example.clock
    events:
      - schedule:
          rate: rate(1s)

Valid time units are "s", "m", "h".

Both execute the function every second.

Parameters

You can inject some parameters using the keyword params in the functions.yml configuration file:

# functions.yml
functions:
  clock:
    handler: example.clock
    events:
      - schedule:
          rate: cron(* * * * *)
          params:
            name1: value1
            name2: value2

Parameters are available in event.params (JS), event['params'] (Python), event->{'params'} (Perl) or event.Params (Go).

Runtimes

You can write functions in JavaScript (Node.js), Python, Perl or Go. You just need to setup the runtime in the configuration of your function.

Available runtimes:

  • nodejs (7.7.4)
  • python-2.7 (2.7.4)
  • python-3.6 (3.6.1)
  • perl (5.24.3)
  • go (1.9.1)

Module dependencies

Node.js modules

Manage Node.js module dependencies.

Python modules

Manage Python module dependencies.

Go packages

Manage Go package dependencies.

Perl modules

Perl dependencies are not yet supported.

If you need one module which is not in the list, please contact us.

Available modules:

  • DBD::MYSQL 4.041: MySQL driver for the Perl5 Database Interface (DBI).
  • DBI 1.636: Database independent interface for Perl.
  • JSON 2.90: JSON (JavaScript Object Notation) encoder/decoder.
  • LWP 6.06: The World-Wide Web library for Perl.
  • Try::Tiny 0.27: Minimal try/catch with proper preservation of $@.
  • XML::Parser 2.44: A perl module for parsing XML documents.

Monitoring

Logs

You can stream functions logs using the cli:

> ovh-functions logs -f

You will be able to follow deployments and deletions:

[2017/05/01 14:33:24] Function hello created
...
[2017/05/01 14:34:21] Function hello updated
...
[2017/05/01 14:35:42] Function hello deleted

And executions:

[2017/05/01 14:29:47] Executing function hello
[2017/05/01 14:29:49] [      hello ] I'm printing hello
[2017/05/01 14:29:49] [      hello ] with World
[2017/05/01 14:29:49] |200| Function hello executed in 116ms
[2017/05/01 14:29:51] Executing function hello
[2017/05/01 14:29:51] [      hello ] I'm printing hello
[2017/05/01 14:29:51] [      hello ] with yo!
[2017/05/01 14:29:51] |200| Function hello executed in 132ms
[2017/05/01 15:29:52] Executing function hello
[2017/05/01 15:29:52] |400| Function hello executed in 122ms

If an error occurred in your code, you will get the stacktrace in the logs.

[2017/05/01 14:32:24] Executing function hello
[2017/05/01 14:32:26] [      hello ] /src/example.js:2
[2017/05/01 14:32:26] [      hello ]   var error = thisvariableisnotdefined;
[2017/05/01 14:32:26] [      hello ]               ^
[2017/05/01 14:32:26] [      hello ]
[2017/05/01 14:32:26] [      hello ] ReferenceError: thisvariableisnotdefined is not defined
[2017/05/01 14:32:26] [      hello ]     at Object.exports.hello (/src/example.js:2:15)
[2017/05/01 14:32:26] [      hello ]     at Object.<anonymous> (/main.js:75:23)
[2017/05/01 14:32:26] [      hello ]     at Module._compile (module.js:571:32)
[2017/05/01 14:32:26] [      hello ]     at Object.Module._extensions..js (module.js:580:10)
[2017/05/01 14:32:26] [      hello ]     at Module.load (module.js:488:32)
[2017/05/01 14:32:26] [      hello ]     at tryModuleLoad (module.js:447:12)
[2017/05/01 14:32:26] [      hello ]     at Function.Module._load (module.js:439:3)
[2017/05/01 14:32:26] [      hello ]     at Module.runMain (module.js:605:10)
[2017/05/01 14:32:26] [      hello ]     at run (bootstrap_node.js:427:7)
[2017/05/01 14:32:26] [      hello ]     at startup (bootstrap_node.js:148:9)
[2017/05/01 14:32:26] |502| Function hello executed in 1.973s

You can also configure a GELF endpoint for your function, all your logs will be automatically forwarded into it:

functions:
  hello:
    runtime: nodejs
    handler: example.hello
    logs:
      - gelf-address: tcp+tls://gra2.logs.ovh.com:12202/?X-OVH-TOKEN=1234-5678-90123-abcd

Or at a higher level for multiple functions to inherit the logs configuration:

provider:
  logs:
    - gelf-address: tcp+tls://gra2.logs.ovh.com:12202/?X-OVH-TOKEN=1234-5678-90123-abcd

functions:
  hello:
    runtime: nodejs
    handler: example.hello
  hello2:
    runtime: nodejs
    handler: example.hello

Metrics

The number of successes and errors of your function executions are available per day and in total:

> ovh-functions metrics
FUNCTION         TOTAL SUCCESSES        TOTAL ERRORS        DAILY SUCCESSES        DAILY ERRORS
pong             107338                 0                   0                      0
ping             106713                 58                  0                      0
mail.send        3                      1                   0                      0
hello            488                    4                   3                      4

Detailed metrics are available through OVH Metrics Data Platform.
Read token:
         WB8Ehw8QAAAabgGPGfq_IkhpkVfcOw82_KrJRhfSdJN1VXE86sCEKaS.BTa12fOwjZuPpgZZzztiyuINAjkcUETfWG4G4oycP8oAfHO3FONJjpOCWgzIuFzGIOPCwA2ra56TUNUe6Ywdwm7iPIb_LbjpFkIKCWQ0SQJ6EIB8iUm.TnphWidxC1BzVGUIP9ZTxiwBi.E7TUo5U7RHRk
Endpoints:
        -  opentsdb.gra1-metrics.metrics.ovh.net
        -  warp.gra1-metrics.metrics.ovh.net
For more information, please visit https://docs.ovh.com/gb/en/cloud/metrics/using/

A query token is given to you, that allows you to run your own queries against OVH Metrics Data Platform. Available metrics:

  • functions.execution.rate (labels: cluster-id, project-id, function-id, status)

  • functions.execution.count (labels: cluster-id, project-id, function-id, status)

  • functions.execution.duration (labels: cluster-id, project-id, function-id, status)

You can visualize these metrics in Grafana, by creating the appropriate datasource and using this dashboard template:

screenshot

Command line reference

OVH Functions provides a command line tool: ovh-functions.

Commands available are:

delete      Delete a function
deploy      Deploy or update a function
describe    Get details about a function
exec        Execute a function
init        Create the hello world example in the current directory
list        List your functions
login       Login to the functions API
logs        Stream the logs of your functions
metrics     Display metrics of your functions
version     Show the cli version information

Limitations

Concurrent executions

Each function is limited by default to 30 parallel executions. See the table below to see the execution rate you can reach depending your function compute time:

Compute time Rate (req/s)
10ms 3000
100ms 300
500ms 60
1s 30
10s 3

If you need higher rates, contact us.

Memory

The memory allocated to a function is 64MB. It is possible to increase it until 512MB, see memory allocation.

Execution time out

By default, if a function is running for more than 30s, it's stopped. You can change this behavior, and configure it between 5s and 300s, see timeout.