Go

Prev Next

Available in Classic and VPC

This document describes how to create and use actions of Go (as a language) format in a variety of ways, and application examples.

Create action

A Go code can have one or more Go source files. The entry point of an action made with Go sources is a function in the main package. The main function's default name is Main, but can be modified to a different name depending on the user's choice. However, the name always starts with an uppercase English letter. The main function needs to be created in the format detailed in the following description:

func Main(event map[string]interface{}) map[string]interface{}

A code written in Go may contain multiple functions, but the main function must be declared as the starting point of a program. With this taken into account, the following is a simple example code hello in Go, printing "Hello World" along with a name and location:

package main
import "log"

func Main(obj map[string]interface{}) map[string]interface{} {
  name, ok := obj["name"].(string)
  if !ok {
    name = "world"
  }
  msg := make(map[string]interface{})
  msg["message"] = "Hello, " + name + "!"
  log.Printf("name=%s\n", name)
  return msg
}

The following is the process of an action called "hello" in the console, using the code written above.

cloudfunctions-exmaple-go_v2_01_ko

Supported formats

The following are formats that can be supported in the execution environment:

  • Executable binary of a Linux ELF execution file compiled for AMD64 architecture
  • ZIP files containing Linux ELF executable files compiled for AMD64 architecture with an executable named exec at the top level
  • Single source file compiled in Go
  • ZIP files that don't contain execution binary files on the top level (folder) are compiled and executed later.
Note

You can cross-compile binaries in the correct format on any Go-supporting platform with GOOS=Linux and GOARCH=amd64. It's safer to use the advance compile feature and use the same compiler as in the execution environment.

Create action using Go Modules

For using external libraries in the go1.19 version, use Go Modules. Without Go Modules, you must include source code directly.
Next is the stepwise guide on how to import the following package in actions using go mod: The relevant method requires Internet communication and NAT G/W settings are required in a VPC environment. For more information about NAT G/W, see NAT Gateway Guide.

import "github.com/rs/zerolog"
  1. Initialize new modules

Initialize new module by executing the following command. Running the command will create a go.mod file.

go mod init <module>
  1. Write main.go

To write main.go, see the following example code:

package main

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

func init() {
	zerolog.TimeFieldFormat = ""
}

func Main(obj map[string]interface{}) map[string]interface{} {
	name, ok := obj["name"].(string)
	if !ok {
		name = "world"
	}
	log.Debug().Str("name", name).Msg("Hello")
	msg := make(map[string]interface{})
	msg["module-main"] = "Hello, " + name + "!"
	return msg
}
  1. Import dependency

Run the command below to update go.mod file and create go.sum file.

go get github.com/rs/zerolog@v1.19.0
  1. Package and upload action

Run the following command to compress the package and create an action by uploading.

zip -r action.zip go.mod go.sum main.go

Create action using packages and vendor

When writing codes, you may need to package dependency files along with one action file. In this case, you can compress related files into one file for packaging and create an action using the compressed file.

The following are the three types available for creating actions using ZIP files:

  • All functionality implemented in main package
  • Some packages separated from main package
  • Implementation including parts with external dependencies (include third party dependencies)

If all features are in a default package, you can place all the source files in the top level of the ZIP file.

Use package folders

If some functionality belongs to packages other than the main function execution part, you must package folders with package names like hello/. The following is a packaging example:

golang-main-package/
- src/
   - main.go
   - hello/
       - hello.go
       - hello_test.go

To run tests and edit without errors, use the src folder. Default package contents go under src/, and hello package source code goes in the hello/ folder. However, you must import subpackages like import "hello". This means setting the src parent directory in your GOPATH for local compilation. If using an editor like VSCode, enable the go.inferGopath option.
When sending the source, compress the src folder's content, rather than the top directory as follows:

cd src
zip -r ../hello.zip *
cd ..

The following is an example code for the above information:

  • src/main.go
package main

import (
	"fmt"
	"hello"
)

// Main forwading to Hello
func Main(args map[string]interface{}) map[string]interface{} {
	fmt.Println("Main")
	return hello.Hello(args)
}
  • src/hello/hello.go
package hello

import (
	"fmt"
)

// Hello receive an event in format
// { "name": "Mike"}
// and returns a greeting in format
// { "greetings": "Hello, Mike"}
func Hello(args map[string]interface{}) map[string]interface{} {
	res := make(map[string]interface{})
	greetings := "world"
	name, ok := args["name"].(string)
	if ok {
		greetings = name
	}
	res["golang-main-package"] = "Hello, " + greetings
	fmt.Printf("Hello, %s\n", greetings)
	return res
}
  • src/hello/hello_test.go
package hello

import (
	"encoding/json"
	"fmt"
)

func ExampleHello() {
	var input = make(map[string]interface{})
	input["name"] = "Mike"
	output := Hello(input)
	json, _ := json.Marshal(output)
	fmt.Printf("%s", json)
	// Output:
	// Hello, Mike
	// {"golang-main-package":"Hello, Mike"}
}

func ExampleHello_noName() {
	var input = make(map[string]interface{})
	output := Hello(input)
	json, _ := json.Marshal(output)
	fmt.Printf("%s", json)
	// Output:
	// Hello, world
	// {"golang-main-package":"Hello, world"}
}

Use vendor folder

When using third-party libraries, the runtime should use vendor folder structure to download and place libraries instead of downloading them through the Internet during compilation. This page provides instructions on how to use the dep tool.
The vendor folder must include the src folder, package folder, and vendor folder. It doesn't work in the top-level folder. To use files included in the main package, place them in a subfolder specified as main, not the top-level folder. For example, let's consider a situation where you need to import packages in file src/hello/hello.go.

import "github.com/sirupsen/logrus"

To create a vendor folder, take the following steps:

  1. Install the dep tool.

  2. Go to src/hello folder.

    cd ./src/hello
    
    • Note that it's src/hello, not src.
  3. RunDEPPROJECTROOT=$(realpath $PWD/../..) dep init.

    • This tool detects used libraries and creates two manifest files, Gopkg.lock and Gopkg.toml.
    • If manifest files already exist, running dep ensure creates the vendor folder and downloads dependencies.

The summarized structure is as follows:

golang-hello-vendor
- src/
    - hello.go
    - hello/
      - Gopkg.lock
      - Gopkg.toml
         - hello.go
         - vendor/
            - github.com/...
            - golang.org/...

The following is an example code for the above information:

  • hello.go
package main

import (
	"fmt"
	"hello"
)

// Main forwading to Hello
func Hello(args map[string]interface{}) map[string]interface{} {
	fmt.Println("Entering Hello")
	return hello.Hello(args)
}
  • hello/hello.go
package hello

import (
	"os"
	"github.com/sirupsen/logrus"
)

var log = logrus.New()

// Hello receive an event in format
// { "name": "Mike"}
// and returns a greeting in format
// { "greetings": "Hello, Mike"}
func Hello(args map[string]interface{}) map[string]interface{} {
	log.Out = os.Stdout
	res := make(map[string]interface{})
	greetings := "world"
	name, ok := args["name"].(string)
	if ok {
		greetings = name
	}
	res["golang-hello-vendor"] = "Hello, " + greetings
	log.WithFields(logrus.Fields{"greetings": greetings}).Info("Hello")
	return res
}

You don't need to save the vendor folder separately because it can be recreated from the version control system. While saving manifest files is sufficient, you must include the vendor folder to create actions in compiled state.
If you want to use third-party libraries in the main function, move main package files at the top level to the main folder to create the vendor folder.