Pivotal’s Function Service and Go (2018-12-15)

Since a few days Pivotal grants access to Pivotal Function Service for early adopters. All what is required for testing PFS is an account at Pivotal Network (which can be setup within minutes) and a request to the get early access for the alpha version of PFS.

PFS installation is quite simple. All what is required is a Kubernetes cluster, which can be easily created by Pivotal’s Container Service (PKS), or alternatively minikube, or GKE can be used.

There are two files to download: the packages and the pfs command line tool (mac, linux,…). Once downloaded and extracted just follow the documentation which explains each of the necessary steps. Basically you need to push the downloaded images to your container registry and then start the installation inside Kubernetes with pfs system install. It automatically installs istio and the knative components on which PFS is build on. The open source project of PFS sponsored by Pivotal is riff.

After installation PFS is ready to use. Three components are important to understand PFS: functions/services, channels for data exchange between the services, and subscriptions for connecting channels with services.

For this early PFS release functions can be written in Java/Spring, JS, or provided as scripts or executables. I went the extra mile and created functions in go which needs to be compiled in executables and registered in PFS as Command functions. Not a big deal. Nevertheless waiting for native support…:)

The first step is setting up your git repo for your functions which my case is here. Your function can be a shell script converting stdin and writing to stdout or any other binary doing the same. For Java you are implementing the Function interface converting a string into another string by an apply() method. In my go example application I’ve a simple wrapper which selects the function which should be executed by an environment variable which I provide during service creation. I’m reading from stdin and write to standard out. Stderr is ignored.

Now you can register the function creating the service reverse calling the binary f which invokes reverse():

pfs function create reverse --git-repo https://github.com/dgruber/f --artifact lxamd64/f 
--image $REGISTRY/$REGISTRY_USER/reverse --env "FUNCTION=reverse" --verbose

Afterwards I can register my other function _upper_ the same way:

pfs function create upper --git-repo https://github.com/dgruber/f --artifact lxamd64/f
--image $REGISTRY/$REGISTRY_USER/upper --env "FUNCTION=upper" —verbose

Note that I’ve setup my image registry beforehand (in my case the REGISTRY env variable is set to _gcr.io_).

In order to list the functions we can do:

pfs service list
reverse RevisionFailed: Revision "reverse-00001" failed with message: "The target is not receiving traffic.".
upper   RevisionFailed: Revision "upper-00001" failed with message: "The target is not receiving traffic.".
pfs service list completed successfully

There is no traffic yet hence nothing is running (scales down to 0).

To test a function we can use the pfs utility with service invoke:

pfs service invoke reverse --text -- -w 'n' -d 'functional test'

curl -H 'Host: reverse.default.example.com' -H 'Content-Type: text/plain' -w 'n' -d 'functional test'
tset lanoitcnuf


The next step is connecting the functions by channels and subscriptions. Channels abstract away from the underlying infrastructure like Kafka or RabbitMQ or the internal test message bus stub:

pfs channel create messages --cluster-bus stub
pfs channel create upperedmessages --cluster-bus stub
pfs subscription create --channel messages --subscriber upper --reply-to upperedmessages
pfs subscription create --channel upperedmessages --subscriber reverse

What we have done now is letting the upper function subscribe to the messages channel and send the output to upperedmessages channel to which the reverse function is subscribed.

Now we have to send a message to the incoming messages channel. This is typically done by an application (see knative serving) but can be tested by creating by accessing a shell in a cluster pod.

I’m following Dave’s recommendation to just do that within a container for testing.

$ kubectl run -it --image=tutum/curl client --restart=Never --overrides='{"apiVersion": "v1", "metadata":{"annotations": {"sidecar.istio.io/inject": "true"}}}'

And then inside the container:

root@client:/# curl -v upperedmessages-channel.default.svc.cluster.local -H "Content-Type: text/plain“ -d mytext

If no pods are running yet they will be started. Yeah!

kubectl get pods

NAME READY STATUS RESTARTS AGE client 2/2 Running 0 30m reverse-00001-deployment-86bf59b96-4pz8l 3/3 Running 0 2m reverse-00001-k9m2q 0/1 Completed 0 1d upper-00001-deployment-6974ccc47d-6lgz7 3/3 Running 0 1m upper-00001-lgtn8 0/1 Completed 0 1d

When having issues with messages or subscriptions a look into the logs stub-clusterbus-dispatcher pod can be very helpful (it runs in the knative-eventing namespace).

That’s it! Happy functioning!