Your first serverless .NET function with OpenFaaS

After reading Alex Ellis’ blog post about creating serverless Python functions using his OpenFaaS framework, I was curious what it would look like to create one using .NET and C#. My main objective was to port my Azure Functions over to Docker containers to help with Docker adoption at my organization (plus help me out with my CI/CD pipelines). We’re using some Azure Functions at work that use Node and C# so my motivation is quite high to try to port those over!

I had spent some time playing around with making some simple Python functions and loved the experience. Create one handler.py and requirements.txt file and you're off to the races!

I did all of the following using a Mac and Visual Studio Code.

Step 0
 If you view the blog post I mention above you can get FaaS and the CLI installed and configured and also get some sample functions working. We’re going to skip to Step 3 but use .NET Core instead of Python.

Step 1 — Write your function

First, create the directory to hold your function:

$ mkdir -p ~/functions/hello-csharp
$ cd ~/functions/hello-csharp

Now create two files: a Function.csproj and a FunctionHandler.cs. You can also clone the FaaS repo from GitHub and copy these two files from the template folder. Here's a link.

Just like Alex’s post, all your functions should be specified in a YAML file. The YAML file tells the FaaS CLI what to build and deploy onto your OpenFaaS cluster.

Create file in the root of the functions folder called stack.yml:

provider:
name: faas
gateway: http://localhost:8080
functions:
hello-csharp:
lang: csharp
handler: ./hello-csharp/
image: faas-hello-csharp

To quote Alex: “Here we can specify a remote gateway if we need to, what the programming language is, and where our handler is located within the filesystem.

Even though Docker is used behind the scenes, you don’t have to write your own Dockerfile unless you want to — you just need to specify an image name.”

Let’s build this thing!

$ faas-cli -action build -f ./stack.yml
...
Successfully tagged faas-hello-csharp:latest  
Image: faas-hello-csharp built.

The Docker Engine will, if necessary, download the template(s) required and then create an image in your local Docker library that contains your compiled .NET code.

You can view it by running the following:

$ docker images | grep faas-hello-csharp
faas-hello-csharp latest e8641cde1273 one minute ago

Next, you have to deploy it locally like so:

$ faas-cli -action deploy -f ./stack.yml
Deploying: hello-csharp.
No existing service to remove
Deployed.
200 OK
URL: http://localhost:8080/function/hello-csharp

Finally, we can test it using curl:

$ curl localhost:8080/function/hello-csharp -d 'FaaS rules!'
Hi there - your input was: FaaS rules!

Pretty cool, eh? Let’s move on to something a little more useful…

Step 2 — External dependencies and JSON

If you’re familiar with .NET Core (since the change from dnx) you know you can add NuGet packages to your csproj file and have them pulled down during build. Let’s add Newtonsoft.Json so we can work with JSON in our function.

$ dotnet add package Newtonsoft.Json --version 10.0.3

Here’s my modified Function.csproj after running that command:

Head to your FunctionHandler.cs and modify it to look like this:

using System;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
namespace Function
{
public class FunctionHandler
{
public void Handle(string input)
{
int value = DateTime.Now.Year;
if (!string.IsNullOrWhiteSpace(input))
{
dynamic jsonResponse = JsonConvert.DeserializeObject(input);
if (jsonResponse.value != null)
{
value = (int) jsonResponse.value;
}
}
            var output = new { factors = GetFactors(value) };
Console.WriteLine(JsonConvert.SerializeObject(output));
}
        private int[] GetFactors(int value)
{
return Enumerable.Range(1, value).Where(i => value % i == 0).ToArray();
}
}
}

We’re using Newtonsoft to parse the input sent to the function, and if a value property is present in the payload we cast that to an int and use that as the value to calculate factors of the number (otherwise we just default it to the current year).

Time to re-build and re-deploy:

$ faas-cli -action build -f ./stack.yml
$ faas-cli -action deploy -f ./stack.yml

Test it!

$ curl localhost:8080/function/hello-csharp -d '{"value":360}'
{"factors":[1,2,3,4,5,6,8,9,10,12,15,18,20,24,30,36,40,45,60,72,90,120,180,360]}
$ curl localhost:8080/function/hello-csharp -d '{"name":"Robbie"}'
{"factors":[1,2017]}

The function isn’t super robust in that if you provide a non-JSON format it errors out, so I’ll leave that exercise to you.

Step 3 — Rejoice (and copy all of your Azure Functions to FaaS)
 This is basically a direct-from-Azure-Functions port of something I wrote in Azure a bit ago. I’ve also written a “Guid.Empty” function as well as Base64 encode and decode functions, all using C# (and then porting them to FaaS).

Feel free to message me on Twitter (@rorpage) if you have any questions! Also, please star the FaaS GitHub repo and read the introduction.

Totally loving @open_faas! I made a shrug function! @alexellisuk #FaaS #Docker pic.twitter.com/dYwv3lfN7w
— Robbie Page (@rorpage) August 16, 2017