Google Cloud Functions

The non-obvious way to call one Google Cloud Function from inside another one.

The Tutorial Made it Seem Simple…

There are many tutorials for Google Cloud Functions. They come in multiple flavors, depending on the goal and the implementation runtime. The tutorials themselves are well-designed and they integrate nicely with the web UI, which makes it easy to get started.

However, for a slightly more complex task, I encountered multiple pitfalls.

Why Are These Called "Functions" Anyway?

The design of these "functions" encourages stateless programming in a minimal environment.

But that's about where the similarities to mathematical or programming language functions end.

These "functions" are not easily composed, but it is possible.

Composing 2 Cloud Functions

If you can compose 2 functions to get a result, then you can compose an unlimited number.

But surprisingly, this was not in the documentation (or I failed to find it).

Essentially, you can call a Cloud Function by its URI, using curl or something similar.

Here's how to do it in Python. Let's start at the end, with the 2nd function which we want to call from the first one.

The second function will just return the string world if the request's message contains hi. Otherwise, it returns the string not implemented:

def world(request):
    if 'hi' in request.get_json()['message']:
        return 'world'
    else:
        return 'not implemented'

This function will be called by the hello function below:

# main.py (function hello)
import requests
def hello(request):
    addr = 'https://us-central1-my-project.cloudfunctions.net/world'
    hdr = {"Content-Type":"application/json"}
    r = requests.post(addr, headers=hdr, data='{"message": "hi"}')
    return r.text

The addr should be the public-facing URL of the world function, after it's been deployed as a Google Cloud Function. In this example, the region is us-central1, and the project name is my-project.

IMPORTANT NOTE!

While Python's Requests documentation states that the data parameter can be a dictionary and it will convert it for you, I found this to be incorrect when using Google Cloud Functions. That's why you see single quotes around the payload in data='{"message": "hi"}' in the hello function body above. My initial attempt to work around this using python's str() function failed, because by default python uses single quotes for the inner quoted values and double quotes for the outer values.

Calling Functions

There are multiple ways to call functions. The easiest is to use the gcloud program1, like this:

gcloud functions call hello --data '{}'

But you can achieve the same result with curl:

curl -X POST 'https://us-central1-my-project.cloudfunctions.net/world' -H "Content-Type:application/json" --data '{}'

Conclusions

As a person who programs with functions extensively, these "Cloud Functions" feel just… wrong to me. Composition is inefficient and awkward.

The ideals of statelessness and functional programming are buried in there somewhere, but the execution of this programming model somehow accumulated a bunch of "cloud" baggage and became heavyweight and inexpressive.

It's a little lighter than a full VM, but so far my experience with Google Cloud products has been that they are incredibly complex but still not very expressive tools. Probably my view is skewed because I'm not trying to build a web-scale PaaS or whatever is cool these days, and so I'm not using these for their intended purpose.