Technology / Tutorials /

Hands-on: Create Your First Serverless Application in Apache OpenWhisk

27 Jan 2017 1:00am, by

An open source serverless platform from IBM, OpenWhisk is available in two modes, hosted and on-premises. Developers can easily get started with it for implementing event-driven, loosely coupled functions. IBM along with Adobe has submitted OpenWhisk to Apache Software Foundation, and was accepted as an incubation project.

Last week, we ran a tutorial on getting OpenWhisk running in the Bluemix cloud environment. This article covers the setup process and the steps in involved in configuring and testing OpenWhisk in your local environment. It then explores the key concepts to create actions, triggers, and rules. Within 45 minutes, you will be able to write and deploy your first serverless application in OpenWhisk.

Apache OpenWhisk’s architecture reflects a modern, containerized, distributed system. It’s fascinating to see how multiple technologies are utilized in designing this first open source serverless platform.

Setting up, Configuring, and Testing Apache OpenWhisk in a VM

Let’s first set up a Vagrant box to run Apache OpenWhisk on a Mac or Windows. It involves configuring both the platform and the command line interface (CLI).

Configuring the platform is simple and straightforward. Just clone the GitHub repo and run a command.

$ git clone https://github.com/openwhisk/openwhisk.git

$ cd openwhisk/tools/vagrant

$ ./hello

Depending on your Internet bandwidth, this process takes approximately 30 minutes to finish. Wait till you see the following output on the screen:

wsk action invoke /whisk.system/utils/echo -p message hello --blocking --result

{

   "message": "hello"

}

Now that OpenWhisk is up and running, let’s go ahead and configure wsk, the CLI. Depending on the platform you are using, download the appropriate binary from the OpenWhisk site.

On a Mac or Linux, run the following commands to add wsk to the path.

$ chmod +x ./wsk

$ sudo mv wsk /usr/local/bin

Before we start using the CLI on the local machine, we need to point it to the Vagrant box. We also need a token to get authenticated with OpenWhisk. We can retrieve that by logging into the Vagrant box.

Run the following commands from the same directory where you cloned the repo.

$ cd tools/vagrant

$ vagrant ssh

$ wsk property get --auth

whisk auth 23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP

$ exit

Copy the above token and run the following command from your Mac:

$ wsk property set --apihost 192.168.33.13 --namespace guest --auth 23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP

The Vagrant box running OpenWhisk is configured to use 192.168.33.13 as the static IP address.

This step configured the CLI for the Vagrant-based OpenWhisk setup. The –apihost switch points to the API endpoint, –namespace represents the username, and –auth carries the unique token. The same CLI can be used with Bluemix-based OpenWhisk by switching these parameters.

To test the setup, run the following commands from your Mac.

$ wsk -i namespace list

namespaces

guest

The above output confirms that OpenWhisk environment is successfully configured. The switch, -i  is required to access insecure endpoints. We can now proceed to the next steps to create an action.

Key Concepts and Terminology

OpenWhisk deals with three key components: Triggers, Actions, and Rules.

Triggers represent specific events like inserting a new record in a database, performing a commit to the source code repository, a sensor reading meeting a specific value. The Trigger is fired by the event source when a specific condition is met.

Actions are the functions that developers write, which get invoked directly through an HTTP call or by a Trigger. OpenWhisk supports JavaScript, Java, Swift, and even arbitrary binaries packaged as Docker containers.

Rules associate Actions with Triggers. They act as the glue binding the code snippets with event sources. One Rule can associate multiple Triggers with an Action.

Creating an Action to Send an Email

In the last tutorial, we explored how to build our first Action in OpenWhisk that returns the weather information of a city. While that was a simple, “hello world” style example, we will now a build a more useful Action that can send an email notification to a user when a specific event takes place.

This sample will demonstrate how to use custom node.js packages to build a function, and invoking it asynchronously.

Since we use SendGrid for sending the email, we need to register and get an API key. After you sign in with your credentials, navigate to the Settings section and click on API Keys. Ensure that you save the API Key when it is displayed. It is shown only the first time it is created.

On your Mac, create a folder and run npm init command in it accepting the default values:

$ mkdir mailfunc

$ cd mailfunc

$ npm init

We will then install the npm for SendGrid that we need in our function.

$ npm install sendgrid -save

Let’s go ahead and create the function. Open your favorite editor and paste the code snippet.

function main(params) {

   return new Promise(function(resolve, reject) {

       if (!params.from || !params.to || !params.subject || !params.content) {

           reject("Insufficient number of parameters");

       }

       var helper = require('sendgrid').mail

       var sg = require('sendgrid')("YOUR_SENDGRID_API_KEY");

       from_email = new helper.Email(params.from)

       to_email = new helper.Email(params.to)

       subject = params.subject
       content = new helper.Content("text/plain", params.content)

       mail = new helper.Mail(from_email, subject, to_email, content)

       var request = sg.emptyRequest({

           method: 'POST',

           path: '/v3/mail/send',

           body: mail.toJSON()

       });

       sg.API(request, function(error, response) {
           if (error) {

               reject(error);

           } else {

               console.log(response.statusCode)

               console.log(response.body)

               console.log(response.headers)

               resolve({

                   msg: "Message sent!"
               });

           }

       })

   });

}

exports.main = main;

The function accepts various parameters that are required to send an email. We will pass those parameters from a Trigger when we invoke the Action.

It’s time for us to package and deploy the function. The following commands will compress all the dependencies into a zip file and deploys them to OpenWhisk:

$ zip -r action.zip *

$ wsk -i action create sendmail --kind nodejs:6 action.zip

ok: created action sendmail

$ wsk -i action list

actions

/guest/sendmail private nodejs:6

Invoking the Action

With the sendmail Action in place, let’s go ahead and invoke it. Start by creating a new folder.

$ mkdir ~/trigger

$ cd ~/trigger

Create a file called parameters.json which contains the parameters required to send an email. Replace the placeholders with appropriate values.

{

 "from": "FROM_EMAIL_ADDRESS",

 "to" : "TO_EMAIL_ADDRESS",

 "subject" : "Alert from OpenWhisk",

 "content" : "Hello Serverless"

}

We can now invoke the Action by passing the parameters file to the function:

$ wsk -i action invoke --blocking --result sendmail --param-file parameters.json

{

"msg": "Message sent!"

}

If you see the above output, it’s an indication that the function was successfully invoked. The recipient mentioned in the parameters.json will receive an email alert.

The –blocking switch forces synchronous invocation of the function. But when an external event source triggers it, the invocation must be asynchronous.

Let’s see how we can asynchronously invoke the same Action. For this, we simply omit the –blocking and –-result switches.

$ wsk -i action invoke sendmail --param-file parameters.json

ok: invoked /guest/sendmail with id 1671372a9f974b9080e24f3cc5081c2a

Notice that OpenWhisk has now returned an activation id, which can be used for tracking the status. We can also list all the activations associated with previous invocations. Even synchronously invoked functions will have an activation id.

$ wsk -i activation list

activations

1671372a9f974b9080e24f3cc5081c2a sendmail

690895452c6c4d719a8dc77301376165 sendmail

The activation with ID 1671372a9f974b9080e24f3cc5081c2a reflects the most recent one. Let’s see the outcome of that activation.

$ wsk -i activation result 1671372a9f974b9080e24f3cc5081c2a

{

   "msg": "Message sent!"

}

Finally, let’s invoke the same Action from cURL.

$ export TYPE='Content-Type: application/json'

$ export AUTH='Authorization: Basic '`wsk property get --auth | awk '{printf("%s", $3)}' | base64 `

$ export NAMESPACE=guest

$ curl -k -s -X POST -d '{"from": "FROM_EMAIL_ADDRESS", "to": "TO_EMAIL_ADDRESS","subject":"Alert from OpenWhisk","content":"Hello Serverless"}' -H "$TYPE" -H "$AUTH" https://192.168.33.13/api/v1/namespaces/$NAMESPACE/actions/sendmail?blocking=true

As you can see, this Action can now be triggered from any event source capable of talking to a REST endpoint. You can easily integrate this function with any application that needs to send a notification as an email.

In the upcoming articles, we will explore the architecture of Apache OpenWhisk. Stay tuned!

IBM is a sponsor of The New Stack.

Feature image via Pixabay.


A digest of the week’s most important stories & analyses.

View / Add Comments