falco_plugin/base/mod.rs
1//! # The common foundation for all Falco plugins
2//!
3//! All plugins must implement the [`Plugin`] trait which specifies some basic metadata
4//! about the plugin.
5//!
6//! See the [`Plugin`] trait documentation for details.
7
8use crate::base::wrappers::BasePluginExported;
9use crate::tables::TablesInput;
10use schema::ConfigSchema;
11use std::ffi::CStr;
12
13mod logger;
14mod metrics;
15mod schema;
16#[doc(hidden)]
17pub mod wrappers;
18
19pub use metrics::{Metric, MetricLabel, MetricType, MetricValue};
20pub use schema::Json;
21
22/// The latest schema supported by the current SDK version
23pub use falco_plugin_api::SCHEMA_VERSION as CURRENT_SCHEMA_VERSION;
24
25/// # A base trait for implementing Falco plugins
26///
27/// There are several constants you need to set to describe the metadata for your plugin, described
28/// below. All the constants are C-style strings: you can initialize the fields with `c"foo"`.
29///
30/// For example, a plugin that doesn't support any capabilities (which is
31/// useless and would fail to load, but is a necessary step to building an actually useful plugin)
32/// might look like:
33///
34/// ```
35/// use std::ffi::CStr;
36/// use falco_plugin::base::{Metric, Plugin};
37/// use falco_plugin::plugin;
38/// use falco_plugin::FailureReason;
39/// use falco_plugin::tables::TablesInput;
40///
41/// // define the type holding the plugin state
42/// struct NoOpPlugin;
43///
44/// // implement the base::Plugin trait
45/// impl Plugin for NoOpPlugin {
46/// const NAME: &'static CStr = c"sample-plugin-rs";
47/// const PLUGIN_VERSION: &'static CStr = c"0.0.1";
48/// const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
49/// const CONTACT: &'static CStr = c"you@example.com";
50/// type ConfigType = ();
51///
52/// fn new(input: Option<&TablesInput>, config: Self::ConfigType)
53/// -> Result<Self, anyhow::Error> {
54/// Ok(NoOpPlugin)
55/// }
56/// }
57///
58/// // generate the actual plugin wrapper code
59/// // note we need to decorate the type name with `#[no_capabilities]` to bypass
60/// // the safeguard against building an invalid plugin
61/// plugin!(#[no_capabilities] NoOpPlugin);
62/// ```
63pub trait Plugin: BasePluginExported + Sized {
64 /// the name of your plugin, must match the plugin name in the Falco config file
65 const NAME: &'static CStr;
66 /// the version of your plugin
67 const PLUGIN_VERSION: &'static CStr;
68 /// a free-form description of what your plugin does
69 const DESCRIPTION: &'static CStr;
70 /// a way to contact you with issues regarding the plugin, be it email or a website
71 const CONTACT: &'static CStr;
72 /// the schema version supported by this plugin
73 ///
74 /// Usually should just be left as the default, but you may need to override it
75 /// if you're using a different version of the falco_event_schema crate.
76 const SCHEMA_VERSION: &'static CStr = CURRENT_SCHEMA_VERSION;
77
78 /// The plugin can be configured in three different ways. In all cases, an instance of the type
79 /// you specify will be passed to the [`Plugin::new`] method.
80 ///
81 /// See <https://falco.org/docs/plugins/usage/> for more information about plugin configuration
82 /// in Falco.
83 ///
84 /// ### No configuration
85 ///
86 /// If your plugin does not need any configuration, set the `ConfigType` to an empty tuple.
87 ///
88 /// ### Configuration as a string
89 ///
90 /// If you set the `ConfigType` to [`String`], your plugin will receive the configuration
91 /// as a string, read directly from the Falco config file.
92 ///
93 /// ### Configuration as JSON
94 ///
95 /// Plugins can also be configured using a JSON object. This will be parsed by the SDK and your
96 /// plugin will receive a data structure containing all the parsed fields. In order to use JSON
97 /// configuration, set the `ConfigType` to `Json<T>`, where the [`Json`](`crate::base::Json`)
98 /// type is provided by this crate and the type `T` must implement [`serde::de::DeserializeOwned`]
99 /// and [`schemars::JsonSchema`].
100 ///
101 /// You will also need to provide a JSON schema for the plugin API to validate the configuration.
102 ///
103 /// Please note that you can use the reexports (`falco_plugin::serde` and `falco_plugin::schemars`)
104 /// to ensure you're using the same version of serde and schemars as the SDK.
105 ///
106 /// Your config struct might look like:
107 ///
108 /// ```
109 /// use falco_plugin::schemars::JsonSchema;
110 /// use falco_plugin::serde::Deserialize;
111 ///
112 /// #[derive(JsonSchema, Deserialize)]
113 /// #[schemars(crate = "falco_plugin::schemars")]
114 /// #[serde(crate = "falco_plugin::serde")]
115 /// struct MyConfig {
116 /// /* ... */
117 /// }
118 /// ```
119 ///
120 /// You can use irrefutable patterns in your `new` and `set_config` methods to make JSON configs
121 /// a little more ergonomic:
122 ///
123 /// ```
124 /// use std::ffi::CStr;
125 /// use anyhow::Error;
126 /// use falco_plugin::base::{Json, Metric, Plugin};
127 ///# use falco_plugin::plugin;
128 /// use falco_plugin::schemars::JsonSchema;
129 /// use falco_plugin::serde::Deserialize;
130 ///
131 /// use falco_plugin::tables::TablesInput;
132 ///
133 /// #[derive(JsonSchema, Deserialize)]
134 /// #[schemars(crate = "falco_plugin::schemars")]
135 /// #[serde(crate = "falco_plugin::serde")]
136 /// struct MyConfig {
137 /// debug: bool,
138 /// }
139 ///
140 /// struct MyPlugin;
141 ///
142 /// impl Plugin for MyPlugin {
143 /// // ...
144 ///# const NAME: &'static CStr = c"";
145 ///# const PLUGIN_VERSION: &'static CStr = c"";
146 ///# const DESCRIPTION: &'static CStr = c"";
147 ///# const CONTACT: &'static CStr = c"";
148 ///
149 /// type ConfigType = Json<MyConfig>;
150 ///
151 /// fn new(input: Option<&TablesInput>, Json(config): Json<MyConfig>) -> Result<Self, Error> {
152 /// // ^^^^^^^^^^^^
153 /// if config.debug { /* ... */ }
154 ///
155 /// // ...
156 ///# todo!()
157 /// }
158 ///
159 /// fn set_config(&mut self, Json(config): Json<MyConfig>) -> Result<(), Error> {
160 /// // ^^^^^^^^^^^^
161 /// if config.debug { /* ... */ }
162 ///
163 /// // ...
164 ///# todo!()
165 /// }
166 ///
167 ///# fn get_metrics(&mut self) -> impl IntoIterator<Item=Metric> {
168 ///# []
169 ///# }
170 ///
171 /// // ...
172 /// }
173 ///# plugin!(#[no_capabilities] MyPlugin);
174 /// ```
175 type ConfigType: ConfigSchema;
176
177 /// This method takes a [`TablesInput`](`crate::tables::TablesInput`) instance, which lets you
178 /// access tables exposed by other plugins (and Falco core).
179 ///
180 /// It should return a new instance of `Self`
181 fn new(input: Option<&TablesInput>, config: Self::ConfigType) -> Result<Self, anyhow::Error>;
182
183 /// Update the configuration of a running plugin
184 ///
185 /// The default implementation does nothing
186 fn set_config(&mut self, _config: Self::ConfigType) -> Result<(), anyhow::Error> {
187 Ok(())
188 }
189
190 /// Return the plugin metrics
191 ///
192 /// Metrics are described by:
193 /// - a name (just a string)
194 /// - a type (monotonic vs non-monotonic: [`crate::base::MetricType`])
195 /// - a value of one of the supported types ([`crate::base::MetricValue`])
196 ///
197 /// **Note**: The plugin name is prepended to the metric name, so a metric called `foo`
198 /// in a plugin called `bar` will be emitted by the plugin framework as `bar.foo`.
199 ///
200 /// **Note**: Metrics aren't registered in the framework in any way and there is no
201 /// requirement to report the same metrics on each call to `get_metrics`. However, it's
202 /// probably a good idea to do so, or at least not to change the type of metric or the type
203 /// of its value from call to call.
204 ///
205 /// There are two general patterns to use when emitting metrics from a plugin:
206 ///
207 /// 1. Predefined metrics
208 /// ```
209 ///# use std::ffi::CStr;
210 ///# use falco_plugin::base::{Metric, MetricLabel, MetricType, MetricValue, Plugin};
211 ///# use falco_plugin::plugin;
212 ///# use falco_plugin::FailureReason;
213 ///# use falco_plugin::tables::TablesInput;
214 ///#
215 ///# // define the type holding the plugin state
216 ///struct MyPlugin {
217 /// // ...
218 /// my_metric: MetricLabel,
219 ///}
220 ///#
221 ///# // implement the base::Plugin trait
222 ///# impl Plugin for MyPlugin {
223 ///# const NAME: &'static CStr = c"sample-plugin-rs";
224 ///# const PLUGIN_VERSION: &'static CStr = c"0.0.1";
225 ///# const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
226 ///# const CONTACT: &'static CStr = c"you@example.com";
227 ///# type ConfigType = ();
228 ///#
229 /// fn new(input: Option<&TablesInput>, config: Self::ConfigType)
230 /// -> Result<Self, anyhow::Error> {
231 /// Ok(MyPlugin {
232 /// // ...
233 /// my_metric: MetricLabel::new(c"my_metric", MetricType::Monotonic),
234 /// })
235 /// }
236 ///
237 ///# fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
238 ///# Ok(())
239 ///# }
240 ///#
241 /// fn get_metrics(&mut self) -> impl IntoIterator<Item=Metric> {
242 /// [self.my_metric.with_value(MetricValue::U64(10u64))]
243 /// }
244 ///# }
245 ///# plugin!(#[no_capabilities] MyPlugin);
246 /// ```
247 ///
248 /// 2. Inline metrics
249 /// ```
250 ///# use std::ffi::CStr;
251 ///# use falco_plugin::base::{Metric, MetricLabel, MetricType, MetricValue, Plugin};
252 ///# use falco_plugin::plugin;
253 ///# use falco_plugin::FailureReason;
254 ///# use falco_plugin::tables::TablesInput;
255 ///#
256 ///# // define the type holding the plugin state
257 ///# struct NoOpPlugin;
258 ///#
259 ///# // implement the base::Plugin trait
260 ///# impl Plugin for NoOpPlugin {
261 ///# const NAME: &'static CStr = c"sample-plugin-rs";
262 ///# const PLUGIN_VERSION: &'static CStr = c"0.0.1";
263 ///# const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
264 ///# const CONTACT: &'static CStr = c"you@example.com";
265 ///# type ConfigType = ();
266 ///#
267 ///# fn new(input: Option<&TablesInput>, config: Self::ConfigType)
268 ///# -> Result<Self, anyhow::Error> {
269 ///# Ok(NoOpPlugin)
270 ///# }
271 ///#
272 ///# fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
273 ///# Ok(())
274 ///# }
275 ///#
276 /// fn get_metrics(&mut self) -> impl IntoIterator<Item=Metric> {
277 /// [Metric::new(
278 /// MetricLabel::new(c"my_metric", MetricType::Monotonic),
279 /// MetricValue::U64(10u64),
280 /// )]
281 /// }
282 ///# }
283 ///# plugin!(#[no_capabilities] NoOpPlugin);
284 /// ```
285 fn get_metrics(&mut self) -> impl IntoIterator<Item = Metric> {
286 []
287 }
288}