Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.

Google Cloud Functions is an event-driven serverless compute platform. Cloud Functions allows you to write your code without worrying about provisioning resources or scaling to handle changing requirements.

There are two types of Cloud Functions:

This codelab will walk you through creating your own Google Cloud Function in Go.

What you will build

In this codelab, you will publish and Cloud Function that when invoked via HTTP will display an image of the Go gopher, designed by Renee French.

What you will learn

What you will need

Start Cloud Shell

While Google Cloud can be operated remotely from your laptop, in this codelab you will be using Google Cloud Shell, a command line environment running in the Cloud.

From the GCP Console click the Cloud Shell icon on the top right toolbar:

It should only take a few moments to provision and connect to the environment. When it is finished, you should see something like this:

This virtual machine is loaded with all the development tools you'll need. It offers a persistent 5GB home directory, and runs on the Google Cloud, greatly enhancing network performance and authentication. All of your work in this lab can be done with simply a browser.

Ensure the Cloud Functions API is enabled

Run the following command from Cloud Shell to make sure the Cloud Functions API is enabled. This will make sure we can deploy Cloud Functions later in the codelab.

gcloud services enable cloudfunctions.googleapis.com

Download and setup the code

  1. From the Cloud Shell terminal, use curl to download a zip with the code for this codelab:
curl -LO https://github.com/GoogleCloudPlatform/golang-samples/archive/master.zip
  1. Use unzip to unpack the code. This unpacks a directory (golang-samples-master), which contains sample Go code for cloud.google.com.
unzip master.zip
  1. Change to the directory containing the code for this codelab:
cd golang-samples-master/functions/codelabs/gopher

The gopher directory contains the following directories and files:

$ tree
.
├── cmd
│   └── main.go     # Binary to run the function locally.
├── go.mod          # Go module definition.
├── gophercolor.png # The gopher!
├── gopher.go       # Go file with the function.
└── gopher_test.go  # Go test file.

HTTP Cloud Functions in Go are written as http.HandlerFunc functions, which is an interface defined in the Go standard library. The function must:

hello.go

package gopher

import (
        "fmt"
        "net/http"
)

// HelloWorld prints "Hello, world."
func HelloWorld(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, world.")
}

You can open this file in Cloud Shell by opening the editor (click the pencil icon at the top right of Cloud Shell) then using the file tree on the left side of the editor to open the file, golang-samples-master/functions/codelabs/gopher/hello.go.

Let's deploy this function as an HTTP Cloud Function! Make sure you cd golang-samples-master/functions/codelabs/gopher/, then you can deploy it using the function name and gcloud functions deploy. This may take a minute or two.

gcloud functions deploy HelloWorld --runtime go111 --trigger-http

Deploying function (may take a while - up to 2 minutes)...done.                
availableMemoryMb: 256
entryPoint: Gopher
httpsTrigger:
  url: https://region-my-project.cloudfunctions.net/HelloWorld
...

To test the HelloWorld function, copy the httpsTrigger URL that's displayed in the gcloud functions deploy output. It will have a form like this:

https://<REGION>-$GOOGLE_CLOUD_PROJECT.cloudfunctions.net/HelloWorld

Then run the following command:

curl https://<REGION>-$GOOGLE_CLOUD_PROJECT.cloudfunctions.net/HelloWorld

Hello, world.

Let's make the "Hello, world" function a bit more entertaining by printing an image of a Gopher for every request:

The following listing shows the code to make it happen. You can see this code in Cloud Shell at golang-samples-master/functions/codelabs/gopher/gopher.go. After the code block, there are notes about each portion.

gopher.go

// Package gopher contains an HTTP function that shows a gopher.
package gopher

import (
        "fmt"
        "io"
        "net/http"
        "os"
)

// Gopher prints a gopher.
func Gopher(w http.ResponseWriter, r *http.Request) {
        // Read the gopher image file.
        f, err := os.Open("gophercolor.png")
        if err != nil {
                http.Error(w, fmt.Sprintf("Error reading file: %v", err), http.StatusInternalServerError)
                return
        }
        defer f.Close()

        // Write the gopher image to the response writer.
        if _, err := io.Copy(w, f); err != nil {
                http.Error(w, fmt.Sprintf("Error writing response: %v", err), http.StatusInternalServerError)
        }
        w.Header().Add("Content-Type", "image/png")
}
  1. The file starts with a package declaration and comment. All Go code is written inside a package, with a declaration like this at the top.
  2. The import block contains a list of other packages that this file depends on. These packages are referred to in the rest of the file using their name. For example, to use the ResponseWriter type from the http package, you write http.ResponseWriter.
  3. Next is the Gopher function declaration.
  4. The function starts by reading the gophercolor.png file (see the sample code) using the os.Open function.
  5. Then, it checks whether there was an error reading the file, which might occur if the file is corrupted or was accidentally left out of the upload.
  6. It uses io.Copy to copy the gopher image to w, the http.ResponseWriter argument. Everything written to w will be sent in the HTTP response.
  7. If there was no error when writing the response, the function returns normally.

Deploy this function as you did the "Hello, world" function from before, using gcloud functions deploy and the name of the function, Gopher:

gcloud functions deploy Gopher --runtime go111 --trigger-http

To test the function, visit the function's URL, which again is displayed in the glcoud functions deploy command output in your browser. If everything is working correctly, you will see the gopher in your browser!

The next step is to add a test to make sure your function continues to work.

HTTP Cloud Functions in Go are tested using the testing and httptest packages from the standard library. There is no need to run an emulator or other simulation to test your function—just normal Go code.

Here is what a test looks like for the Gopher function:

gopher_test.go

package gopher

import (
        "net/http"
        "net/http/httptest"
        "testing"
)

func TestGopher(t *testing.T) {
        rr := httptest.NewRecorder()
        req := httptest.NewRequest("GET", "/", nil)
        Gopher(rr, req)
        if rr.Result().StatusCode != http.StatusOK {
                t.Errorf("Gopher StatusCode = %v, want %v", rr.Result().StatusCode, http.StatusOK)
        }
}
  1. Go tests are written the same way as other Go files. They start with a package declaration and set of imports.
  2. The test declaration is of the form func MyTest(t *testing.T). It must be exported and take one argument of type *testing.T.
  3. The test creates test response and request values using the httptest package.
  4. It calls the Gopher function.
  5. After invoking the Gopher function, the test checks the HTTP response code to be sure there were no errors.

To run these tests locally, cd to the directory with the files you're testing then use the go test command:

go test -v

=== RUN   TestGopher
--- PASS: TestGopher (0.00s)
PASS
ok          github.com/GoogleCloudPlatform/golang-samples/functions/codelabs/gopher 0.037s

Next, you'll create a binary (package main, in Go) so that you can run your function locally and try it in a browser.

You can run an HTTP function locally by creating an HTTP server and registering your function as a handler. (See the Writing Web Applications tutorial for an in-depth guide to writing a full web application in Go.)

You can write an HTTP server for your function in a subdirectory of your function. Following a Go convention, you name that directory cmd and create a main.go file inside it:

cmd/main.go

// The cmd command starts an HTTP server.
package main

import (
        "fmt"
        "log"
        "net/http"

        "github.com/GoogleCloudPlatform/golang-samples/functions/codelabs/gopher"
)

func main() {
        http.HandleFunc("/", gopher.Gopher)
        fmt.Println("Listening on localhost:8080")
        log.Fatal(http.ListenAndServe(":8080", nil))
}
  1. This file uses package main as the package. A main package will be built as a binary that you can run.
  2. This file imports github.com/GoogleCloudPlatform/golang-samples/functions/codelabs/gopher, based on the module line of the go.mod next to your function file. When you're writing your own functions, you can name the module whatever you prefer.
  3. The func main() is the entry point for the binary. It registers the gopher.Gopher function as an HTTP handler then starts the server using http.ListenAndServe.

To build and run this binary locally, run the following commands:

GO111MODULES=on # Turn on Go modules.
go build -o start ./cmd
./start

Listening on localhost:8080

Because the function loads the gophercolor.png image from the current working directory, you have to start your binary from the same directory as the gophercolor.png file. The -o start flag says to name the output binary start. The ./cmd says to build the binary located in the cmd directory.

Now use the Cloud Shell Web Preview to test the server in your browser. Click the Web Preview button and then select port 8080 from the displayed menu. Cloud Shell opens the preview URL on its proxy service in a new browser window. The web preview restricts access over HTTPS to your user account only. If everything is working properly, you should see the Go Gopher!

Cloud Functions pricing is based on how often your function is invoked, including a free tier for functions that don't run often.

Once you're done testing your Cloud Functions, you can delete it using gcloud:

gcloud functions delete Gopher
gcloud functions delete HelloWorld

You can also delete the function from the Cloud Console UI.

We hope you enjoy using Cloud Functions in Go!