How to create a plugin
This page will guide you through the process of creating a plugin.
Programming Language
A plugin can be created in virtually any language (C/C++, Rust, Python, Go and many other languages) as plugins are built using WebAssembly (wasm).
Interfaces
A plugin, regardless of the langage of implementation, needs to expose the following interfaces:
Interface | What it does |
---|---|
create(config, name, params) -> result<string, string> | Create a detection on the remote system |
read(config, name, params) -> result<string, string> | Get/Read a detection from the remote system |
update(config, name, params) -> result<string, string> | Update a detection on the remote system |
delete(config, name, params) -> result<string, string> | Remove a detection from the remote system |
load() -> metadata | Load the plugin and return its metadata |
settings() -> string | Returns the expected configuration/ KCL ? |
schema() -> string | Return the schema ? |
ping(config) -> result<bool: string> | Open a connection to the remote system |
Details
Plugin interfaces are defined in the Plugin Wasm Interface Type (WIT). Always refer to this file to ensure your interfaces return the appropriate data types.
Python plugin
This section will guide you through the creation of a python plugin. We'll target Splunk for demonstration purpose but note that we have an official Splunk plugin written in Rust.
Python Global Interpreter (GIL)
As a python developper, your probably know that python is a slow language and that's mainly a consequence of the Python Global Interpreter (GIL). Guess what? Compiling your python code in WebAssembly will actually make it fly!
Package manager
In this guide, we'll use Poetry to manage python packages but feel free to use your prefered package manager, pip, conda or anything else.
First things first, we create our new python app
poetry new pysplunk
Dependencies
Then, we need to install componentize-py
, a tool to convert a Python application to a WebAssembly component.
cd pysplunk
poetry add componentize-py
WIT
Next, we copy the wit files from logcraft-cli into our python app. These files are basically defining the contracts between the cli and the plugin.
~$ cd pysplunk
~$ cp -r ~/logcraft-cli/wit pysplunk/wit
~$ ls -1 pysplunk/wit
world.wit
plugin.wit
~$
Bindings
The step is optional as it only creates python bindings in the working directory to integrate with your IDE. When we'll build the application, these bindings will also be generated automatically.
poetry run componentize-py --wit-path pysplunk/wit --world plugins bindings pysplunk
This will result in a new directory plugins
that contains the bindings in pysplunk
.
App
Then, we'll create a main.py
that will hold the main code for our plugin where we'll implement the interfaces expected by the WIT files.
#!/usr/bin/env python3
from plugins import Plugins
from plugins.exports.plugin import Metadata
from plugins.types import Err, Ok, Some, Result
class Plugin(Plugins):
def load(self) -> Metadata:
return Metadata("my-plugin", "0.1.0", "The Batman", "This is a famous plugin")
def settings(self):
pass
def schema(self):
pass
def create(self, config: str, name: str, params: str):
pass
def read(self, config: str, name: str, params: str):
pass
def update(self, config: str, name: str, params: str):
pass
def delete(self, config: str, name: str, params: str):
pass
def ping(self, config: str) -> int:
pass
The class name is important as it is derivated from the WIT files, hence your plugin must start with:
class Plugin(Plugins):
Naming Convention (kebab-case)
Regarding the load()
function, make sure that the name given to your plugin respect kebab-case (i.e. all lowercase, alphanumerical and hyphens between words), here my-plugin
, as this will end up in your lgc.yaml
file!
Finally, compile the plugin:
poetry run componentize-py --wit-path pysplunk/wit --world plugins componentize -p pysplunk main -o my-plugin.wasm
Congratulations, your plugin can now be installed in LogCraft CLI:
lgc plugins install my-plugin.wasm