Native Extensions
A native extension follows the base rules of a template module, but it may not contain any templates. This means a template module CANNOT contain a native extension.
The major difference between a module and a native extension is that instead of a templates/
directory a plugin/
directory is used to store the source code of the plugin.
Creating a Native Extension
For details on how to create a native extension, check out the getting started documentation.
Fetching a Native Extension
By default a native extension is fetched from Github releases using semantic-versioning. A binary must exist on the Github release matching these specific formats.
Testing a Native Extension
Currently stencil does not provide a testing framework for native extensions, but the recommend approach would be to use the snapshot testing framework provided by stencil or to build a system outside of stenciltest for this.
A native extension can be ran locally using the replacements
key in an application's manifest (stencil.yaml
), which is described in the module documentation. However, when doing this the native extension must write it's binary to bin/plugin
.
Debugging a Native Extension
The go-plugin
library does not surface errors to stencil. Instead, it will raise the generic message failed to create connection to extension: Unrecognized remote plugin message
. To determine a more precise error message, execute the native extension binary directly. The binary path can usually be found in bottom of the returned error. If not, the binary lives in the bin/plugin
subdirectory of the native extension folder.
How Native Extensions Work
Native extensions are implemented using the go-plugin using the net/rpc
transport layer. go-plugin, in simple terms, implements this by executing a plugin and negotiating with it to create a unix socket to communicate over with the native extension.
Once a connection has been established stencil communicates with the plugin over the following RPC methods (in Go this is the Implementation
interface).
GetTemplateFunctions
RPC is called, which returns a list of declared functions this native extension implements. The format for this is defined asTemplateFunction
in Go.- Stencil creates a wrapper go-template function to call the
ExecuteTemplateFunction
rpc based on the data returned byGetTemplateFunctions
. The function is exposed atimportPath.function
via theextensions.Call
method. - When the function is called via
extensions.Call "importPath.function"
the rpcExecuteTemplateFunction
is called with the arguments passed to the function. The format for this RPC is defined asTemplateFunctionExec
in Go. - The response from the RPC is returned directly to the go-template with no processing.
TIP
While ExecuteTemplateFunction
's return value is a interface{}
, that is actually just a wrapper around the lower level implementation dubbed as implementationTransport
. The return value of that is []byte
, which is expected to be valid JSON to be decoded by stencil before being passed to the template that called it. This is done to ensure typed data is always able to be sent over.