Trying again with Golang

Let's try creating the next function with another runtime, Go. Unlike Java, Go codes inside an Fn function do not have a proper concept of EntryPoint. Fortunately, the execution model of Fn is simple enough, so this matter is extremely trivial:

$ fn init --runtime go hello_go

Creating function at: /hello_go
Runtime: go
Function boilerplate generated.
func.yaml created.

Here's the list of files for the Go Fn function:

$ cd hello_go

$ tree .
.
├── func.go
├── func.yaml
└── test.json

The file, func.go is, of course, the function program itself, while func.yaml is the Fn's function descriptor. And the one interesting file here is test.json – a file containing test fixtures for functional tests. Currently, we can use the fn test command to test the positive paths, but not the negative results.

We will take a look at func.yaml to see what's inside it first. The version will be automatically increased every time it is deployed. The runtime here is go as we specified it as the --runtime parameter of fn init. The entrypoint here should not be touched. Just leave it there, trust me:

$ cat func.yaml

version: 0.0.1
runtime: go
entrypoint: ./func

The Go codes could consume the STDIN directly. The best way is to pass the input as a JSON and use Go's encoding/json package to process the data. Here's the example adapted from the original Fn example. This program was modified to simplify the output process and add error checking and logging:

package main

import (
"encoding/json"
"fmt"
"os"
)

type Message struct {
Name string
}

func main() {
m := &Message{Name: "world"}

err := json.NewDecoder(os.Stdin).Decode(m)
if err != nil {
fmt.Fprintf(os.Stderr, "err JSON Decode: %s ", err.Error())
os.Exit(250)
}

fmt.Printf(`{"success": "Hello %s"}`, m.Name);
os.Exit(0)
}

In every program, we need to check errors and handle them. As shown in the previous example, we check errors occurring during encoding and then print the error message to os.Stderr, the standard error file in Go. Then we just exit the process with code > 0. Here, we use 250.

Let's summarize error handling and logging in Fn. First, write messages to STDERR and they will be stored in the logs. Second, exit the process with an error code, that is, > 0. Fn will then mark the container execution as error.

Let's see this in action. Make sure we have the previous code example inside func.go and deploy it with the fn deploy command:

$ fn deploy --app demo --registry chanwit

Deploying hello_go to app: demo at path: /hello_go
Bumped to version 0.0.2
Building image chanwit/hello_go:0.0.2 .......
Pushing chanwit/hello_go:0.0.2 to docker registry...The push refers to repository [docker.io/chanwit
/hello_go]
00a6a1467505: Pushed
96252b84ae14: Pushed
97dedccb7128: Mounted from fnproject/go
c9e8b5c053a2: Mounted from fnproject/go
0.0.2: digest: sha256:8a57737bff7a8e4444921959532716654230af0534b93dc6be247ac88e4e7ef2 size: 1155
Updating route /hello_go using image chanwit/hello_go:0.0.2...

If the last line of fn deploy is saying that the route is updated, it will be good to go.

Next, we will use the fn call command to invoke the function, which is now registered as a route under the app demo. Try calling it without parameters to cause the error:

$ fn call demo /hello_go 

{"error":{"message":"container exit code 250"}}
ERROR: error calling function: status 502

This is what we would expect. It was a call without input. So the encoding/json raised the error and the program wrote a log message in STDERR (not shown in the previous code). Finally, the function returns 250. With the message, we saw the fn call printed out, saying the function container exited with code 250. So the error was handled properly.

No log messages here, but we will get back to them later.

Next, we will make a successful call. To make it green, just pass the JSON body using the echo command. The JSON body will be piped to fn call  and turned into a HTTP request, then it will be received by the Fn Server and serialized again to be STDIN for the function program.

The success JSON chuck is what we would expect for a program working correctly.

The syntax of calling a remote function via fn call is that we need to pass the application name and the route name for it to invoke:

$ echo '{"Name": "chanwit"}' | fn call demo /hello_go 

{"success": "Hello chanwit"}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset