Google Cloud Functions is a compute solution offering from Google that is described as lightweight and event-based. It allows you to create small, single-purpose functions that respond to cloud events without the need to manage a runtime environment or a server. Cloud Functions however is an alpha release that might be changed in backward-incompatible way and it requires the corresponding Google Cloud Platform account to be whitelisted.
Cloud Functions code are written in JavaScript and executed on Google Cloud Platform in a managed Node.js environment. The code can be triggered asynchronously by events from Google Cloud Storage and Google Cloud Pub/Sub. The code can also be triggered synchronously by HTTP invocation.
Getting Started
Install the Google Cloud SDK: brew cask install google-cloud-sdk. This includes the gcloudtool that provides the primary command-line interface to the Google Cloud Platform. Authenticate the gcloud tool with your Google Cloud Platform account: gcloud init. Lastly, install the alpha commands component: gcloud components install alpha.
Using the web console, create a new project, e.g. DevWorld. And set it as the default project:
$ gcloud projects list
PROJECT_ID NAME PROJECT_NUMBER
devworld-1470605832118 DevWorld 386816325421
$ gcloud config set project devworld-1470605832118
Lastly, go to the Cloud Functions web console to enable the API.
Git Repository
When we create a new project, we get a Git repository that we can use to store our files; so that we don’t need to use a Cloud Storage bucket. Note that if you install the SDK with Homebrew Cask, you need to rename the git-credential-gcloud file or create a soft link to git-credential-gcloud.sh, e.g. cd ~/.homebrew/bin/ && ln -s git-credential-gcloud git-credential-gcloud.sh
Commit the code, push it to the remote repository, and deploy the code by executing: gcloud alpha functions deploy helloMessageFunction --source-url https://source.developers.google.com/p/devworld-1470605832118/r/default --source /helloMessage --trigger-http --source-branch master --entry-point helloMessage. This creates a new function with the name helloMessageFunction that uses the code in helloMessage module in the index.js file in the /helloMessage folder. The function can be invoked synchronously via HTTP methods.
We can test the function by directly triggering the function with the gcloud tool: gcloud alpha functions call helloMessageFunction --data '{"name":"Bill"}'. Or, by using curl: curl -X POST https://us-central1-devworld-1470605832118.cloudfunctions.net/helloMessageFunction -H "Content-Type:application/json" --data '{"name":"Steve"}'.
Cloud Functions Types
There are two distinct Cloud Functions types: HTTP (foreground) functions and background functions.
HTTP Functions
HTTP functions are used when we want to invoke them via standard HTTP requests with various methods like: GET, PUT, POST, DELETE, and OPTIONS. The function signature takes HTTP-specific arguments: request and response. The arguments have properties of ExpressJSRequest and Response objects that are used to extract and return data. Note that when the function has completed, we need to call a termination method such as send(), json(), or end(). Otherwise, the function may continue to run and be terminated by the system platform.
Background Functions
Background functions are used when we want the Cloud Functions code to be invoked because of a change in a Cloud Storage bucket or a message on a Pub/Sub topic.The function signature takes two arguments: context and data. As in the case with HTTP function, when the background function has completed, we need to call a termination method such as success(), failure(), or done(). Otherwise, the function may continue to run and be terminated by the system platform. The context parameter holds the information about the execution environment and it also includes callback functions to signal completion of the background function. And the data parameter contains the data associated with the event that triggered the function execution.
IBM Bluemix is a cloud platform as a service developed by IBM. Think of it as an alternative to Amazon Web Services or Google Cloud Platform. Bluemix is developed using Cloud Foundry which is basically a software stack to build your own PaaS (another alternative to Cloud Foundry is OpenStack).
OpenWhisk is a distributed event-driven programming service. It provides programming model to upload event handlers to a cloud service, and register the handlers to respond to various events. The events can come from various Bluemix services like Cloudant, external sources, or direct invocations from web or mobile apps over HTTP.
With OpenWhisk, developers only pay for what they use and they don’t have to manage a server. However, OpenWhisk is still an experimental service from IBM that might change in ways that are incompatible with earlier versions.
Getting Started
We assume that the default organisation and space have been set during the IBM Bluemix account signup process.
Install the wsk command line tool: pip install --upgrade https://new-console.ng.bluemix.net/openwhisk/cli/download. Set the OpenWhisk namespace and authorisation key: wsk property set --apihost openwhisk.ng.bluemix.net --auth XXXX:XXXX --namespace "namespace_dev" where the required information can be found on the OpenWhisk web console. Verify the setup: wsk action invoke /whisk.system/samples/echo -p message hello --blocking --result which performs a synchronous blocking invocation of echo with hello as an argument.
Key Concepts
There are several key concepts and terminologies in OpenWhisk:
Trigger: a class of events emitted by event sources.
Action: the actual code to be executed whenever a trigger fires. It can be a snippet of JavaScript or Swift code, or a custom binary in a Docker container.
Rule: an association between a trigger and an action.
Feed: a code that configures an external event source to fire trigger events.
Package: a bundle of feeds and actions that describe a service in a uniform manner.
OpenWhisk Actions
OpenWhisk supports action code written in Swift that will be executed in a Linux environment. However, be aware that the version of Swift on Linux that is used with OpenWhisk might be different with versions of Swift from stable releases of Xcode. Create the following greetSomeone.swift:
Note that Swift actions always consume a dictionary and produce a dictionary. You can create the OpenWhisk action by executing: wsk action create greetSomeone greetSomeone.swift and invoking it by executing: wsk action invoke --blocking greetSomeone --param name Tim
The --result flag makes the invocation shows only the result (it requires --blocking flag). If you don’t need the result right away, you can make a non-blocking invocation:
$ wsk action invoke greetSomeone --param name Tim
ok: invoked greetSomeone with id e106be8bf30d4eb598c8bb5141a1b00c
$ wsk activation result e106be8bf30d4eb598c8bb5141a1b00c
{"greeting": "Hello Tim!"}
Default Parameters
OpenWhisk allows us to set default parameters for action invocations. As an example, we can set default parameters for the previous example: wsk action update greetSomeone --param name Steve and invoking it:
Note that it achieves the same thing as the Swift default parameters in functions although it is at a different abstraction level.
Action Sequences
OpenWhisk allows us to create an action from a sequence of actions. Think of it like a pipeline of OpenWhisk actions. This allows us to create abstractions of common functions and compose them to build the desired actions. For example, write the following code as sort.swift and create a new OpenWhisk action: wsk action create sort sort.swift:
A trigger can be thought of as a named channel for a class of events. A trigger can be activated or fired by an event which is a dictionary of key-value pairs. Each firing of a trigger results in an activation ID. A trigger can be fired by a user or by an external event source. A feed is a way to configure an external event source to fire a trigger.
A rule associates one trigger with one action. Every firing of the trigger causes the action to be executed with the event as the input. If a trigger is fired without an accompanying rule to match against then it has no visible effect.
As an example, create the following action helloAction.swift:
The above command creates a named channel to which events can be fired. We need to create a rule to observe the effect: wsk rule create --enable myRule eventUpdate helloAction
In a new terminal window, we could poll for the rule invocation: wsk activation poll helloAction. When we fire a trigger event, e.g. wsk trigger fire eventUpdate --param name "Rob". We could see the activation ID and payload as follows:
$ wsk activation poll helloAction
Hit Ctrl-C to exit.
Polling for logs
Activation: helloAction (4542fc011e214fe19ad94d2de583b5c4)$ wsk activation result 4542fc011e214fe19ad94d2de583b5c4
{"payload": "Rob"}
We can create multiple rules that associate one trigger with different actions. The trigger and action that define a rule must be in the same namespace and cannot belong to a package. If an action belongs to a package, we need to copy it into our namespace first, e.g. wsk action create echo --copy /whisk.system/samples/echo.
API Calls
OpenWhisk actions that we have created can be called directly without using the OpenWhisk CLI. It requires the auth key and token that can be obtained by executing: wsk property get --auth. The strings before and after the colon are the key and token, respectively. We could take a look at the HTTP request and response by using the -v flag. For example:
All OpenWhisk APIs are protected with HTTP Basic authentication. At the moment, OpenWhisk supports only one key per account. Be aware that your key would need to be embedded in client-side code making it visible to the public.
OpenWhisk iOS SDK
OpenWhisk provides an iOS SDK that allows apps to easily invoke actions and fire remote triggers. It is written in Swift 2.2 and supports iOS 9 and later versions. The SDK can be installed by using Carthage, CocoaPods, or from the source directory. We could get started by getting the starter app example, installing the dependencies, and taking a look at the code:
$ mkdir iOS ; cd iOS
$ wsk sdk install iOS
$ pod install
Take a look at the ViewController.swift file and set the OpenWhisk credentials according to the output of wsk property get --auth.
On Sunday, 8th of May 2016, I started reading about Elm. I was interested because of its functional reactive programming nature and it’s one of the inspirations (though not primary) for ReactiveCocoa and Redux. I started reading about its syntax, its architecture, looking at a few examples, and watching several awesomevideos about Elm. I especially like the concept of signals and mailboxes. It’s not a foreign concept if you’re coming from ReactiveCocoa or other FRP frameworks. In Elm, signals felt natural and it fit nicely with the language (I believe it’s due to the fact that Elm is functional rather than imperative). Elm signals did not feel bolted-on like in ReactiveCocoa.
Well, on Tuesday (10th of May 2016), signals were removed from Elm with the post titled A Farewell to FRP. I was completely surprised and dumbstruck. But not for long, the more I read and internalise the post and the discussion on Elm slack channel, it became clear that subscriptions in Elm 0.17 was actually a really big deal and it simplified a lot of things. Rather than setting up the signals wiring, subscriptions let our components sit around and wait for messages, whether it’s from WebSocket, the clock, etc. And it becomes the underlying library code’s responsibility to handle a bunch of tricky resource management stuff behind the scenes (e.g. WebSocket connection). I think it’s a win for users.
Our team used IdeaBoardz to conduct our retros and I thought that its UI could be improved . Thus, I decided to spend the following week after my learning to build Elmütt that is a fresh clone of IdeaBoardz written in Elm. It uses Elm 0.17 and Bootstrap 4 for the front end; and Python 2.7, Flask, Redis, and WebSocket for the back end. And it is deployable to Heroku.
I am really happy with Elm and it is such a breath of fresh air. Below is my notes that I took whilst learning about Elm. I think you should try Elm.
Elm
This text file describes the Elm programming language, specifically version 0.17. It was designed by Evan Czaplicki to provide “the best of functional programming in your browser”. Elm is about:
No runtime errors in practice. No null. No undefined is not a function.
Elm is functional, no state and no side effects. Elm is a strongly typed language with static typing, similar to Haskell and other ML inspired languages.
mkdir elm
cd elm
elm package install
elm package install elm-lang/html
touch Main.elm
Edit the file Main.elm to contain the following code:
module Main exposing (main)
import Html
main = Html.text "Hello World"
Compile the file: elm make Main.elm and open the resultant index.html. Alternatively, run elm reactor and go to http://localhost:8000. Unfortunately, it doesn’t support live reload. To get the live reload functionality, install elm-server and run elm-server Main.elm.
The Elm Architecture
The Elm Architecture is the recommended pattern for building Elm applications. It’s a unidirectional architecture that consists of three components:
- Model is a type defining the data structure of the state.
- View is a function that transforms state into rendering (e.g. HTML).
- Update is a function from previous state and an action to new state. An action is a type that encapsulate a user action or an external event.
In code, it looks like the following:
module Main exposing (main)
import Html
import Html.App
main : Program Never
main =
Html.App.beginnerProgram
{ model = "Hello, world!" , view = Html.text , update = identity }
Or, in the expanded form:
module Main exposing (main)
import Html exposing (Html, text, div)
import Html.App
type alias Model = String
model : Model
model = "Hello, world!"
view : Model -> Html Msg
view model =
div [] [ text (toString model) ]
type Msg = NoOp
update : Msg -> Model -> Model
update msg model =
model
main : Program Never
main =
Html.App.beginnerProgram
{ model = model
, view = Html.text
, update = update
}