content/manual/plugins-dev.md
As mentioned before in this manual, plugins are the thing that make CoreDNS tick. We've seen a bunch of configuration in the previous section, but how can you write your own plugin?
See Writing Plugins for CoreDNS for an older post on this subject. The plugin.md documented in CoreDNS' source also has some background and talks about styling the README.md.
The canonical example plugin is the example plugin. Its github repository shows the most minimal code (with tests!) that is needed to create a plugin.
It has:
setup.go and setup_test.go, which implement the parsing of configuration from the Corefile.
The (usually named) setup function is called whenever the Corefile parser see the plugin's
name; in this case, "example".example.go (usually named <plugin_name>.go), which contains logic for handling the query, and
example_test.go, which has basic units tests to check if the plugin works.README.md that documents in a Unix manual style how this plugin can be configured.The code also has extensive comments; feel free to fork it and base your plugin off of it.
When CoreDNS wants to use a plugin it calls the method ServeDNS. ServeDNS has three parameters:
context.Context;dns.ResponseWriter that is basically the client's connection;*dns.Msg that is the request from the client.ServeDNS returns two values: a (response) code and an error. The error is logged when the
errors is used in this server.
The code tells CoreDNS if a reply has been written by the plugin chain or not. In the latter case, CoreDNS will take care of that. For the code's values, we reuse the DNS return codes (rcodes) from the dns package.
CoreDNS treats:
As special and will then assume nothing has been written to the client. In all other cases, it assumes something has been written to the client (by the plugin).
See this post on how to compile CoreDNS with your plugin.
Use the log package to add logging to your plugin. You initialize it with:
var log = clog.NewWithPlugin("example")
Now you can log.Infof("...") to print something to standard output prefixed with level [INFO] plugin/example. Level can be: INFO, WARNING or ERROR.
In general, logging should be left to the higher layers when returning an error. However, if there is a reason to consume the error but still notify the user, then logging in the plugin can be acceptable.
When exporting metrics, the Namespace should be plugin.Namespace (="coredns"), and the
Subsystem should be the name of the plugin. The README.md for the plugin should then also contain
a Metrics section detailing the metrics. If the plugin supports readiness
reporting, it should also have a Ready section detailing it.
Each plugin should have a README.md explaining what the plugin does and how it is configured. The file should have the following layout:
<plugin name> - <one line description>., i.e. NAME DASH DESCRIPTION DOT.More sections are, of course, possible.
We use the Unix manual page style:
*plugin*.**EXAMPLE**.[optional].arg....literal.Please be sure to use example.org or example.net in any examples and tests you provide. These
are the standard domain names created for this purpose. If you don't, there is a chance your fantasy
domain name is registered by someone and will actually serve web content (which you may like or not).
In a perfect world, the following would be true for plugins: "Either you are responsible for a zone or
not". If the answer is "not", the plugin should call the next plugin in the chain. If "yes" it
should handle all names that fall in this zone and the names below - i.e. it should handle the
entire domain and all sub domains, also see here
on how a query is process with fallthrough enabled.
. {
file example.org db.example
}
In this example the file plugin is handling all names below (and including) example.org. If
a query comes in that is not a subdomain (or equal to) example.org the next plugin is called.
Now, the world isn't perfect, and there are good reasons to "fallthrough" to the next middleware, meaning a plugin is only responsible for a subset of names within the zone. The first of these to appear was the reverse plugin, now replaced with the generalized template plugin that can synthesizes various responses.
The nature of the template plugin might only deal with specified record TYPEs, and then only
for a subset of the names. Ideally, you would want to layer template in front of another
plugin such as file or auto. This means template could handle some special
reverse cases and all other requests are handled by the backing plugin. This is exactly what
"fallthrough" does. To keep things explicit we've opted that plugins implementing such behavior
should implement a fallthrough keyword.
The fallthrough directive should optionally accept a list of zones. Only queries for records
in one of those zones should be allowed to fallthrough.
See this document describing the requirements.