add rules and (de)/serialization, wip
This commit is contained in:
parent
804faa6273
commit
cb93615a98
234
src/api/rules.rs
234
src/api/rules.rs
@ -136,3 +136,237 @@ impl ApiClient<Ruleset> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
domain_bulk_read!(Ruleset, RulesetQuery);
|
domain_bulk_read!(Ruleset, RulesetQuery);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
|
||||||
|
pub struct Rule {
|
||||||
|
pub ruleset: String,
|
||||||
|
pub folder: String,
|
||||||
|
pub folder_index: usize,
|
||||||
|
pub properties: RuleProperties,
|
||||||
|
pub value_raw: String,
|
||||||
|
pub conditions: RuleCondition
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
|
||||||
|
pub struct RuleProperties {
|
||||||
|
pub description: String,
|
||||||
|
pub comment: String,
|
||||||
|
pub documentation_url: String,
|
||||||
|
pub disabled: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement
|
||||||
|
pub trait RuleMatch {
|
||||||
|
fn matches(&self, other: Self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
|
||||||
|
pub struct RuleCondition {
|
||||||
|
pub host_name: MatchCondition,
|
||||||
|
pub host_tags: Vec<HostTagCondition>,
|
||||||
|
pub host_label_groups: Vec<LabelGroupCondition>,
|
||||||
|
pub service_label_groups: Vec<LabelGroupCondition>,
|
||||||
|
pub service_description: MatchCondition
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
|
||||||
|
pub struct MatchCondition {
|
||||||
|
match_on: Vec<String>,
|
||||||
|
operator: MatchOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum MatchOperator {
|
||||||
|
#[default]
|
||||||
|
OneOf,
|
||||||
|
NoneOf
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
|
||||||
|
pub struct HostTagCondition {
|
||||||
|
key: String,
|
||||||
|
match_on: TagMatchOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
|
||||||
|
pub enum TagMatchOperator {
|
||||||
|
Is(String),
|
||||||
|
IsNot(String),
|
||||||
|
OneOff(Vec<String>),
|
||||||
|
NoneOff(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
|
||||||
|
pub struct LabelGroupCondition {
|
||||||
|
operator: LabelOperator,
|
||||||
|
label_group: Vec<LabelCondition>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
|
||||||
|
pub struct LabelCondition {
|
||||||
|
operator: LabelOperator,
|
||||||
|
label: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
|
||||||
|
pub enum LabelOperator {
|
||||||
|
#[default] And, Or, Not
|
||||||
|
}
|
||||||
|
|
||||||
|
mod serde_tags {
|
||||||
|
use serde::{Deserialize, Serialize, de::{self, Visitor, Unexpected, Error}, ser::SerializeStruct};
|
||||||
|
|
||||||
|
use crate::api::rules::{HostTagCondition, TagMatchOperator};
|
||||||
|
|
||||||
|
impl Serialize for HostTagCondition {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer {
|
||||||
|
let mut map = serializer.serialize_struct("HostTagCondition", 3)?;
|
||||||
|
|
||||||
|
map.serialize_field("key", &self.key)?;
|
||||||
|
|
||||||
|
match &self.match_on {
|
||||||
|
TagMatchOperator::Is(value) => {
|
||||||
|
map.serialize_field("operator", "is")?;
|
||||||
|
map.serialize_field("value", &value)?;
|
||||||
|
},
|
||||||
|
TagMatchOperator::IsNot(value) => {
|
||||||
|
map.serialize_field("operator", "is_not")?;
|
||||||
|
map.serialize_field("value", &value)?;
|
||||||
|
},
|
||||||
|
TagMatchOperator::OneOff(items) => {
|
||||||
|
map.serialize_field("operator", "one_of")?;
|
||||||
|
map.serialize_field("value", items)?;
|
||||||
|
},
|
||||||
|
TagMatchOperator::NoneOff(items) => {
|
||||||
|
map.serialize_field("operator", "none_of")?;
|
||||||
|
map.serialize_field("value", items)?;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
map.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HostTagConditionVisitor;
|
||||||
|
|
||||||
|
impl <'de> Visitor<'de> for HostTagConditionVisitor {
|
||||||
|
type Value = HostTagCondition;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("expected a struct with fields key, operator and value")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: serde::de::MapAccess<'de>, {
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
enum Field {
|
||||||
|
Key,
|
||||||
|
Operator,
|
||||||
|
Value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
enum Operator {
|
||||||
|
Is, IsNot, OneOff, NoneOff
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum ValueKind {
|
||||||
|
Single(String),
|
||||||
|
Multiple(Vec<String>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValueKind {
|
||||||
|
fn into_single(self) -> Option<String> {
|
||||||
|
if let Self::Single(s) = self {
|
||||||
|
Some(s)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn into_multiple(self) -> Result<Vec<String>, String> {
|
||||||
|
match self {
|
||||||
|
Self::Multiple(ss) => Ok(ss),
|
||||||
|
Self::Single(s) => Err(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tkey: Option<String> = None;
|
||||||
|
let mut toperator: Option<Operator> = None;
|
||||||
|
let mut tvalue: Option<ValueKind> = None;
|
||||||
|
|
||||||
|
while let Some(key) = map.next_key()? {
|
||||||
|
match key {
|
||||||
|
Field::Key => {tkey.insert(map.next_value()?);},
|
||||||
|
Field::Operator => {toperator.insert(map.next_value()?);},
|
||||||
|
Field::Value => {tvalue.insert(map.next_value()?);}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let key = tkey.ok_or(A::Error::missing_field("key"))?;
|
||||||
|
let match_on = match toperator.ok_or(de::Error::missing_field("operator"))? {
|
||||||
|
Operator::Is => TagMatchOperator::Is(
|
||||||
|
tvalue.and_then(|v| v.into_single())
|
||||||
|
.ok_or(A::Error::invalid_value(
|
||||||
|
Unexpected::Seq,
|
||||||
|
&"single string"
|
||||||
|
))?
|
||||||
|
),
|
||||||
|
Operator::IsNot => TagMatchOperator::IsNot(
|
||||||
|
tvalue.and_then(|v| v.into_single())
|
||||||
|
.ok_or(A::Error::invalid_value(
|
||||||
|
Unexpected::Seq,
|
||||||
|
&"single string"
|
||||||
|
))?
|
||||||
|
),
|
||||||
|
Operator::OneOff => TagMatchOperator::OneOff(
|
||||||
|
tvalue.ok_or(A::Error::missing_field("value"))?
|
||||||
|
.into_multiple()
|
||||||
|
.map_err(|s| A::Error::invalid_value(
|
||||||
|
Unexpected::Str(s.as_str()),
|
||||||
|
&"sequence of stirngs"
|
||||||
|
))?
|
||||||
|
),
|
||||||
|
Operator::NoneOff => TagMatchOperator::NoneOff(
|
||||||
|
tvalue.ok_or(A::Error::missing_field("value"))?
|
||||||
|
.into_multiple()
|
||||||
|
.map_err(|s| A::Error::invalid_value(
|
||||||
|
Unexpected::Str(s.as_str()),
|
||||||
|
&"sequence of stirngs"
|
||||||
|
))?
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(HostTagCondition { key, match_on })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <'de> Deserialize<'de> for HostTagCondition {
|
||||||
|
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de> {
|
||||||
|
deserializer.deserialize_map(HostTagConditionVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -8,13 +8,11 @@ const PASSWORD: &str = "C5t71DUPAu2D";
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let log_config = simplelog::ConfigBuilder::new()
|
|
||||||
.set_location_level(simplelog::LevelFilter::Error)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
simplelog::TermLogger::init(
|
simplelog::TermLogger::init(
|
||||||
simplelog::LevelFilter::Trace,
|
simplelog::LevelFilter::Trace,
|
||||||
log_config,
|
simplelog::ConfigBuilder::new()
|
||||||
|
.set_location_level(simplelog::LevelFilter::Error)
|
||||||
|
.build(),
|
||||||
simplelog::TerminalMode::Stderr,
|
simplelog::TerminalMode::Stderr,
|
||||||
simplelog::ColorChoice::Auto,
|
simplelog::ColorChoice::Auto,
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user