A plugin adds functionality to CoreDNS, i.e. caching, metrics and basic zone file serving are all plugins.
If you want to write a new plugin and want it to be included by default, i.e. merged in the code base please open an issue first to discuss initial design and other things that may come up. Starting with a README file to explain how things work from a user perspective is usually a good idea.
See the example plugin for, uh, an example for how to structure, write and test a plugin. There are plenty of comments in the code to help you along.
How to Register a CoreDNS Plugin?
When writing your plugin code you will need to register it with CoreDNS. This can be done by calling the following function:
func init() { plugin.Register("foo", setup) }
Every plugin must have a name, foo
, in this case. When foo
is encountered in the configuration
the setup
function will be called in this package.
The Setup Function
The setup
function (it may be called different, but pretty much every plugin just calls it
setup
) parses the configuration and populates internal data structures.
The setup function a caddy.Controller
and returns an error: (We use
plugin.Error to prefix returned error
with plugin/foo:
to improve error reporting).
func setup(c *caddy.Controller) error {
if err != nil {
return plugin.Error("foo", err)
}
// various other code
return nil
}
If we see a line in the Corefile such as:
foo gizmo
We can get the value of the first argument (“gizmo”) like so:
for c.Next() { // Skip the plugin name, "foo" in this case.
if !c.NextArg() { // Expect at least one value.
return c.ArgErr() // Otherwise it's an error.
}
value := c.Val() // Use the value.
}
You parse the tokens present for your plugin by iterating over c.Next()
which is true as long
as there are more tokens to parse. Since a plugin may appear multiple times, you must iterate over
c.Next()
to get all the appearances of your plugin and consume the tokens.
Adding to CoreDNS
To plug your plugin into CoreDNS, put in
plugin.cfg
and run go generate
.
If you’re working with an external, non core, plugin it will be easiest to make a symlink from the plugin directory to your plugin. I.e.
cd plugin
ln -s ../../example .
cd ..
vi plugin.cfg # add example:example
go generate
go build
How A Plugin Works in CoreDNS
Check out the godoc for the plugin package. The most important type is plugin.Handler.
A Handler
is a function that handles a DNS request. CoreDNS will do all the bookkeeping of setting
up an DNS server for you, but you need to implement these two types.
Writing a Handler
plugin.Handler
is an interface similar to http.Handler
except that it deals with DNS and the
ServeDNS
method returns (int, error)
. The int
is status code, and the error
is logged (if
not nil) See plugin.md for more details
about these return values.
Handlers are usually a struct with at least one field, the next Handler in the chain:
type MyHandler struct {
Next plugin.Handler
}
To implement the plugin.Handler
interface, we write a method called ServeDNS
. This method is the
actual handler function, and, unless it fully handles the request by itself, it should call the next
handler in the chain:
func (h MyHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
return h.Next.ServeDNS(ctx, w, r)
}
The interface also needs a method func Name() string
.,
func (h MyHandler) Name() string { return "foo" }
That’s all there is to it (apart from writing all code that actually does something with the DNS request of course).
Further Reading
Simple examples of plugin that can be found in CoreDNS are:
- root; does not register itself as a plugin. It simply performs some setup.
- chaos; a DNS plugin that
responds to
CH txt version.bind
requests. - example; an example plugin that prints “example” when responding to a query.
Don’t forget: the best documentation is the godoc and the code itself!