Spacebear Blog

Transform Drone extensions into OpenFaaS functions

Published: July 28, 2020

Drone is a CI/CD server that can be extended to handle many different specific use-cases, however the Drone Server communicates with extensions via Webhooks which require you to manage a separate web process for each extension. Merging a drone extension into an OpenFaaS function removes a lot of the maintenance cost of using Drone Extensions.

This tutorial assumes you have an OpenFaaS install that you are able to deploy functions, and an already written Drone extension.

To get started, you’ll need to pull the golang-middleware template, and create a new function.

$ faas template store pull golang-middleware
$ faas new --lang golang-middleware <fn-name>

Where you’ll end up with a function.go like:

package function
import (
	"fmt"
	"net/http"
)
func Handle(w http.ResponseWriter, r *http.Request) {
	// ...
}

Drone has several different types of extensions available to you, however for this tutorial we will focus on the configuration extensions. Please note that this process is the same for other extensions as well.

Now that we have the base function.go read to go, which accepts w http.ResponseWriter, r *http.Request as arguments, we will import our drone extensions, and pass the two arguments coming into the Handler function to the ServeHTTP function provided by Drone that validates the incoming request, and passes the request to the extension.

package function
import (
	"fmt"
	"net/http"

+	"github.com/drone/drone-go/plugin/config"
+	plugin "go.spacebear.ee/example"
)
func Handle(w http.ResponseWriter, r *http.Request) {
+	secret := "this is a shared secret string"
+	handler := config.Handler(
+		plugin.New(),
+		secret,
+		nil,
+	)
+	handler.ServeHTTP(w, r)
}

The above assumes you’ve already written your Drone extension, and it is located at go.spacebear.ee/example.

You’ll also notice a hardcoded string that is the secret, which isn’t the best way to handle this secret information. So our next step is to use the secret handling capabilities from OpenFaaS. In the example we will now replace the hardcoded secret with one that we store in kubernetes, docker swarm, or however you have your secret stored. We assume your secret is named drone_extension_secret, although you can change the path below to suit your needs.

Note: The path to the secret changed, and so the path below won’t work if you are using OpenFaaS version 0.8.2 or below.

package function
import (
	"fmt"
	"net/http"
+	"io/ioutil"
+	"strings"

	"github.com/drone/drone-go/plugin/config"
	plugin "go.spacebear.ee/example"
)
func Handle(w http.ResponseWriter, r *http.Request) {
-	secret := "this is a shared secret string"
+	secretBytes, _ = ioutil.ReadFile("/var/openfaas/secrets/drone_extension_secret")
+	secret := strings.TrimSpace(string(secretBytes))
	// ...
}

To send your secret to OpenFaaS so it can be used in your function you’ll need to first create a file named drone_extension_secret.txt, and place your shared secret.

$ faas-cli secret create drone_extension_secret --from-file=drone_extension_secret.txt

Now that your drone extension is now an OpenFaaS function, and OpenFaaS knows your shared secret the function will need to be deployed.

$ faas-cli deploy --filter <fn-name> --secret drone_extension_secret

Now that your function is being managed by OpenFaaS you’ll need to tell Drone all about it, this is as easy as setting two environment variables on your Drone server.

DRONE_YAML_ENDPOINT=https://openfaas.example.com/function/<fn-name>
DRONE_YAML_SECRET=<shared-secret-as-defined-by-you>

These environment variables are for the configuration extension, however you can refer to the Drone documentation about extensions to learn about the other environment variables to use for other extensions. The others are also an endpoint environment variable, and one for the shared secret.

Future

You could combine the previous blog post of using Drone to deploy functions to OpenFaaS to deploy extensions that manage the same Drone server.