Here is the 3rd part of our blogpost series about building Furnace. In the first part we talked about the AWS services used in the brief, the second part was about AWS Go SDK and we began dissecting the intricacies of Furnace. This episode is discussing the experimental plugin system of Furnace.

Go Experimental Plugins

Since Go 1.8 was released, an exciting and new feature was introduced called a Plug-in system. This system works with dynamic libraries built with a special switch to go build. These libraries, .so or .dylib (later), are loaded and once that succeeds, specific functions can be called from them (symbol resolution). We will see how this works. For package information, visit the plugin packages Go doc page here.

Furnace Plugins

So, what does Furnace use plugins for? Furnace utilizes plugins to execute arbitrary code in, currently, four given locations/events. These are: pre_create; post_create; pre_delete; post_delete. These events are called- as their name suggests- before and after the creation and deletion of the CloudFormation stack. It allows the user to execute some code without having to rebuild the whole project. It does this by defining a single entry point for the custom code called RunPlugin. Any number of functions can be implemented, but the plugin MUST provide this single, exported function. Otherwise, it will fail and ignore that plugin.

Using Plugins

It’s super easy to implement and use these plugins. I’m not going to go into details of how to load them because that is done by Furnace; I will be focusing instead on how to write and use them.
To use a plugin, create a go file called: 0001_mailer.go. The 0001 before it will define WHEN it’s executed. Having multiple plugins is completely okay. Execution of order however, depends on the names of the files. Now, in the 0001_mailer.post_create, we would see something like this:
package main

  1. import "log"
  2.  
  3. // RunPlugin runs the plugin.
  4. func RunPlugin() {
  5.     log.Println("My Awesome Pre Create Plugin.")
  6. }

The next step is to build this file to be a plugin library. Please note: Right now, this only works on Linux! To build this file run the following:
go build -buildmode=plugin -o 0001_mailer.pre_create 0001_mailer.go
The important part here is the extension of the file specified with -o. It’s important because that’s how Furnace identifies what plugins it has to run. Finally, copy this file to ~/.config/go-furnace/plugins and you are all set to go. 

Slack Notification Plugin

To demonstrate how plugin can be used, if you need some kind of notification once a Stack is completed. For example: you might want to send a message to a Slack room. To do this, your plugin would look something like this:
package main

  1. import (
  2.     "fmt"
  3.     "os"
  4.  
  5.     "github.com/nlopes/slack"
  6. )
  7.  
  8. func RunPlugin() {
  9.     stackname := os.Getenv("FURNACE_STACKNAME")
  10.     api := slack.New("YOUR_TOKEN_HERE")
  11.     params := slack.PostMessageParameters{}
  12.     channelID, timestamp, err := api.PostMessage("#general", fmt.Sprintf("Stack with name '%s' is Done.", stackname), params)
  13.     if err != nil {
  14.         fmt.Printf("%s\n", err)
  15.         return
  16.     }
  17.     fmt.Printf("Message successfully sent to channel %s at %s", channelID, timestamp)
  18. }

Currently, Furnace has no ability to share information of the stack with an outside plugin. Thus ‘Done’ could be anything from Rollback to Failed to CreateComplete.

Closing Words

That’s it for plugins. Thanks a lot for reading! Gergely.