Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Serverless Swift on OpenWhisk

DZone's Guide to

Serverless Swift on OpenWhisk

In this article, you'll learn step-by-step how to create a serverless application using the Swift language, in the OpenWhisk platorm.

· Web Dev Zone
Free Resource

Never build auth again! Okta makes it simple to implement authentication, authorization, MFA and more in minutes. Try the free developer API today! 

I'm interested in serverless computing and as I write Swift, the OpenWhisk platform comes up high when you Google. This turns out to be a really good choice as OpenWhisk is Open Source so I can read the source code (and have done!). In principle, I can also run my own instance of it if I need to for regulatory reasons, or just to avoid vendor lock-in.

Commercially, the whole point of Serverless (aka Functions as a Service) is that it deals with everything infrastructure related other than the function I am writing. So I actually host my OpenWhisk functions with IBM's Bluemix.

In a serverless environment, we write separate functions that are event driven. Each function can even be in a different language as they are all independent. Also, our functions are stateless which, as an API person, I'm comfortable with. There's more than one way to trigger a function, but I've started with the simplest: an HTTP request.

Far more information is in the docs and I can't recommend the OpenWhisk-Team Slack channel enough; very helpful people on there.

This is my intro post on getting going with OpenWhisk (which I wrote mainly so that all the info I need is in one place)!

Notes on Getting Started

There's plenty of blog posts about getting started with OpenWhisk on Bluemix, so this is mostly an aide-memoire for myself as I had to go back on a couple of things that I didn't understand the first time.

Create a Bluemix Account:

  • Log into your Bluemix account or create one.
  • OpenWhisk is only provisioned in the southern region of the United States.
  • Make a note of your organization and space, you'll need them later (if you're setting up Bluemix for the first time, call your first space "dev").

Set up OpenWhisk:

  • Go to https://console.ng.bluemix.net/openwhisk/
  • Click "Download OpenWhisk CLI" and install the wsk executable.
  • Make sure you run the "New Authentication" and "Unset Namespace" commands in your terminal.

You should now have a working wsk command line tool. wsk is remarkably helpful. Add -h and it'll give you help.

First Swift Action

As OpenWhisk is serverless, we have a single entry function to an action. Swift has this signature:

func main(args: [String:Any]) -> [String:Any]

This means that we receive a dictionary of arguments (which are called parameters elsewhere in OpenWhisk) and must return a dictionary (in Swift, a dictionary is what is called an associative array or hash in other languages). The returned dictionary is the data returned to the caller.

First Action

To create an action, we need a swift source file. This can have any name, but my general rule of thumb is to name it the same as the action name. As OpenWhisk looks quite API-ish, so we'll create a "ping" action; thus our file is called ping.swift

ping.swift:

func main(args: [String:Any]) -> [String:Any] {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    let now = formatter.string(from: Date())

    return ["ack": now]
}

Packages

Always put your actions into packages. These work a little like namespaces, in that you can group together related actions, triggers, rules, and what not. You can also attach default parameters to packages that are then available to every action, which can be very useful. Interestingly, you can "import" a namespace into another one (known as binding) and when you do so, you can override the parameter values for that package for this specific binding.

To Create a Package Called "P1":

$ wsk package create P1

Upload to OpenWhisk

To upload your function to OpenWhisk:

$ wsk action update P1/ping ping.swift

(You can also use create in place of update, but as update will create the action if it doesn't
exist, you may as well just always use the name update.)

Viewing All Actions

To view your actions, list them:

$ wsk action list
actions
/19FT_dev/P1/ping                                 private swift:3

In this case, I have one action list. It's fully qualified name is, "/19FT_dev/P1/ping", and as it's part of a private package. It's private and written in Swift 3. The language information is provided as OpenWhisk supports Java, NodeJS, and Python actions in addition to Swift.

Running the Action

There are many ways to run the action. The first way is to use the wsk tool's action invoke command:

$ wsk action invoke --blocking --result P1/ping
{
    "ack": "2017-02-26 11:03:59"
}

The blocking parameter tells the command to wait until the action completes before returning. If you leave it out, then the action is invoked, but you don't get the result as it's in "fire and forget" mode.

Alternatively, you can use curl.

To do this, you make a POST request to https://openwhisk.ng.bluemix.net/api/v1/namespaces/{NAMESPACE}/actions/{ACTION}?blocking=true with your API key in the Authorization header. As Basic Auth requires the credentials to be Base64 encoded, the easiest way to get the information in the right format is:

$ wsk property get --auth | awk '{printf("%s", $3)}' | openssl base64 | tr -d "\n"

You also need your namespace, which has the format of {organisation name}_{space name} as you can see in the fully qualified action name in the output of wsk action list. In my case, this is, 19FT_dev.

We can then use this with our curl command:


$ AUTH=$(wsk property get --auth | awk '{printf("%s", $3)}' | openssl base64 | tr -d "\n")
$ curl -X POST -H "Authorization: Basic $AUTH" \
https://openwhisk.ng.bluemix.net/api/v1/namespaces/19FT_dev/actions/P1/ping?blocking=true

{
  "duration": 2448,
  "name": "ping",
  "subject": "rob@19ft.com",
  "activationId": "21f3c9b1bcad40fc84e0fbcf7fb356cd",
  "publish": false,
  "annotations": [{
    "key": "limits",
    "value": {
      "timeout": 60000,
      "memory": 256,
      "logs": 10
    }
  }, {
    "key": "path",
    "value": "19FT_dev/P1/ping"
  }],
  "version": "0.0.9",
  "response": {
    "result": {
      "ack": "2017-02-25 23:17:53"
    },
    "success": true,
    "status": "success"
  },
  "end": 1488064673564,
  "logs": [],
  "start": 1488064671116,
  "namespace": "19FT_dev"
}

As you can see, you get a lot of info back, but the key bit is in the response -> result property:

"result": {
    "ack": "2017-02-25 23:17:53"
}

As you don't want to share your API key with anyone, there are other ways to call this action via HTTP: Web Action and API Gateway. We'll explore these in a separate post.

Something Wrong? Viewing the Logs

If something goes wrong, the place to look is the logs. To get an ongoing, up-to-date view, open a new terminal window and run this in it:

$ wsk activation poll

This works a lot like tail -f. Invoke your action and you'll see the information for it.

Alternatively, to view the last log, read LornaJane's "One-Line Command For Newest OpenWhisk Logs" article.

The command you need is:

$ wsk activation list -l1 | tail -n1 | cut -d ' ' -f1 | xargs wsk activation logs

That's a bit of mouthful, so put it in a script or alias it.

Fin

That's it. Getting started with Swift actions on OpenWhisk is remarkably easy and lots of fun. If you want to poke around a more fully featured app, have a look at my DrinkChooser project.

Launch your application faster with Okta’s user management API. Register today for the free forever developer edition!

Topics:
swift ,web dev ,bluemix

Published at DZone with permission of Rob Allen, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}