Skip to content

Extending Casket

Jason Chu edited this page Oct 25, 2020 · 3 revisions

Casket can be extended with plugins. Plugins add missing functionality to Casket. They are "plugged in" at compile-time.

Almost everything in Casket is a plugin. The HTTP server is a plugin. Casket's advanced TLS features are plugins. Every single directive you type in the Casketfile is a plugin.

Some plug into Casket core, while others plug into a specific server type. Some plugins even plug into other plugins (for example, DNS providers plug into the tls plugin). Casket ships with certain plugins by default: the HTTP server with its standard directives and the standard Casketfile loaders.

There are different kinds of plugins you can write:

  • Server Types - A kind of server that Casket can run (HTTP and DNS for example)
  • Directives - A directive for the Casketfile
  • HTTP Middleware - A function that handles HTTP requests, usually invoked by a Casketfile directive
  • Casketfile Loader - Customize how the Casketfile is loaded
  • DNS Provider - Make ACME's DNS challenge compatible with your DNS provider
  • Listener Middleware - Wrap a net.Listener with your own Listener to do something at the transport or protocol layer
  • Event Hook - Execute a function when the Casket process emits certain events
  • Cluster Support - Storage backends that enable sharing resources behind load balancers or in a cluster

The links above will show you how to write those kinds of plugins, but always refer to the godoc and the code base itself when you have questions!

5 Steps to Make a Casket Plugin

Even though there are different kinds of plugins, the process of creating one is roughly the same for all.

1. Create a package and register your plugin.

Start a new Go package with an init function and register your plugin with Casket or the server type or the other plugin that uses it. The registration function you use depends on the kind of plugin. A few are shown here:

import "github.com/tmpim/casket"
import "github.com/tmpim/casket/caskettls"

func init() {
	// register a "generic" plugin, like a directive or middleware
	casket.RegisterPlugin("name", myPlugin)

	// register a plugin that can load the Casketfile when Casket starts
	casket.RegisterCasketfileLoader("name", myLoader)

	// register a plugin that implements an entire server type
	// for use with Casket
	casket.RegisterServerType("name", myServerType)

	// register a function that runs when Casket emits events
	casket.RegisterEventHook("eventName", myHookFn)

	// register a cluster plugin
	caskettls.RegisterClusterPlugin("name", constructorFn)

	// add a function that wraps listeners for the HTTP server
	// (it's more common for a directive to call this rather than a standalone plugin)
	httpserver.AddListenerMiddleware(myListenerMiddleware)

	// ... there are others. See the godoc.
}

Every plugin must have a name and, when applicable, the name must be unique for that server type.

2. Plug in your plugin.

To plug your plugin into Casket, import it. This is usually done near the top of run.go:

import _ "your/plugin/package/path/here"

3. Test, test, test!

Write tests. Get good coverage where possible, and make sure your assertions test what you think they are testing! Use go vet and go test -race to ensure your plugin is as error-free as possible.

4. Maintain your plugin.

People will use plugins that are useful, clearly documented, easy to use, and maintained by their owner.

And congratulations, you're a Casket plugin author!