Skip to content

Commit

Permalink
Add postprocessing for static monitors, see #33
Browse files Browse the repository at this point in the history
  • Loading branch information
BigBoot committed Apr 28, 2024
1 parent cd7aa5b commit 766ddbb
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`kuma.mygroup.group.name: "This is a {{ Group }}"` -> `kuma.mygroup.group.name: "{% raw %}This is a {{ Group }}{% endraw %}"`
- You are using [Snippets](https://github.com/BigBoot/AutoKuma#snippets) with arguments:
The syntax for snippet arguments changed from `{{@0}}`, `{{@1}}`, `{{@2}}` etc. to `{{args[0]}}`, `{{args[1]}}`, `{{args[2]}}` etc.
- Static Monitors will be be postprocessed in the same way as monitors defined by container labels, see [#33](https://github.com/BigBoot/AutoKuma/issues/33)

### Fixed
- autokuma: connections not being closed when an error occurs during setup, see [#11](https://github.com/BigBoot/AutoKuma/issues/11)
Expand Down
62 changes: 39 additions & 23 deletions autokuma/src/sync.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
config::{Config, DeleteBehavior},
error::{Error, KumaError, Result},
util::{group_by_prefix, ResultLogger},
util::{group_by_prefix, FlattenValue as _, ResultLogger},
};
use bollard::{
container::ListContainersOptions, service::ContainerSummary, Docker
Expand All @@ -13,6 +13,7 @@ use kuma_client::{
Client,
};
use log::{info, warn};
use serde_json::json;
use tera::Tera;
use std::{
collections::{BTreeMap, HashMap}, env, error::Error as _, sync::Arc, time::Duration
Expand Down Expand Up @@ -55,7 +56,7 @@ impl Sync {
.map_err(|e| Error::LabelParseError(format!("{}\nContext: {:?}", e.source().unwrap(), &template_values.get("container"))))
}

fn get_defaults(&self, monitor_type: impl AsRef<str>) -> Vec<(String, String)> {
fn get_defaults(&self, monitor_type: impl AsRef<str>) -> Vec<(String, serde_json::Value)> {
vec![
self.defaults.get("*"),
self.defaults.get(monitor_type.as_ref()),
Expand All @@ -64,7 +65,8 @@ impl Sync {
.flat_map(|defaults| {
defaults
.into_iter()
.map(|entry| entry.to_owned())
.map(|entry| entry.into_iter()
.map(|(key, value)| (key.to_owned(), json!(value))))
.collect_vec()
})
.flatten()
Expand Down Expand Up @@ -93,23 +95,23 @@ impl Sync {
.collect::<Vec<_>>())
}

fn get_monitor_from_labels(
fn get_monitor_from_settings(
&self,
id: &str,
monitor_type: &str,
settings: Vec<(String, String)>,
settings: Vec<(String, serde_json::Value)>,
template_values: &tera::Context,
) -> Result<Monitor> {
let defaults = self.get_defaults(monitor_type);

let config = Self::fill_templates(
vec![("type".to_owned(), monitor_type.to_owned())]
vec![("type".to_owned(), json!(monitor_type.to_owned()))]
.into_iter()
.chain(settings.into_iter())
.chain(defaults.into_iter())
.sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
.unique_by(|(key, _)| key.to_owned())
.map(|(key, value)| format!("{} = {}", key, toml::Value::String(value)))
.map(|(key, value)| format!("{} = {}", key, value))
.join("\n"),
template_values,
)?;
Expand Down Expand Up @@ -172,7 +174,10 @@ impl Sync {
.map(|(key, value)| (key, group_by_prefix(value, ".")))
.flat_map(|(id, monitors)| {
monitors.into_iter().map(move |(monitor_type, settings)| {
self.get_monitor_from_labels(&id, &monitor_type, settings, template_values)
self.get_monitor_from_settings(&id, &monitor_type, settings.into_iter()
.map(|(key, value)| (key, json!(value)))
.collect_vec(), template_values
)
.map(|monitor| (id.clone(), monitor))
})
})
Expand Down Expand Up @@ -263,33 +268,47 @@ impl Sync {
.collect::<HashMap<_, _>>())
}

async fn get_monitor_from_file(file: impl AsRef<str>) -> Result<Option<(String, Monitor)>> {
async fn get_monitor_from_file(&self, file: impl AsRef<str>) -> Result<(String, Monitor)> {
let file = file.as_ref();
let id = std::path::Path::new(file)
.file_stem()
.and_then(|os| os.to_str().map(|str| str.to_owned()))
.ok_or_else(|| Error::IO(format!("Unable to determine file: '{}'", file)))?;

Ok(if file.ends_with(".json") {
let content = tokio::fs::read_to_string(file)
let value: Option<serde_json::Value> = if file.ends_with(".json") {
let content: String = tokio::fs::read_to_string(file)
.await
.map_err(|e| Error::IO(e.to_string()))?;
Some((
id,

Some(
serde_json::from_str(&content)
.map_err(|e| Error::DeserializeError(e.to_string()))?,
))
)
} else if file.ends_with(".toml") {
let content = tokio::fs::read_to_string(file)
.await
.map_err(|e| Error::IO(e.to_string()))?;
Some((
id,

Some(
toml::from_str(&content).map_err(|e| Error::DeserializeError(e.to_string()))?,
))
)
} else {
None
})
};

let values = value
.ok_or_else(||Error::DeserializeError(format!("Unsupported static monitor file type: {}, supported: .json, .toml", file)))
.and_then(|v| v.flatten())?;

let monitor_type = values.iter()
.find(|(key, _)| key == "type")
.and_then(|(_, value)| value.as_str().map(|s| s.to_owned()))
.ok_or_else(|| Error::DeserializeError(format!("Static monitor {} is missing `type`", file)))?;

let context = tera::Context::new();
let monitor = self.get_monitor_from_settings(&id, &monitor_type, values, &context)?;

Ok((id, monitor))
}

fn merge_monitors(
Expand Down Expand Up @@ -374,11 +393,8 @@ impl Sync {
.await
.map_err(|e| Error::IO(e.to_string()))?
{
if let Some((id, monitor)) =
Self::get_monitor_from_file(f.path().to_string_lossy()).await?
{
new_monitors.insert(id, monitor);
}
let (id, monitor) = self.get_monitor_from_file(f.path().to_string_lossy()).await?;
new_monitors.insert(id, monitor);
} else {
break;
}
Expand Down
35 changes: 35 additions & 0 deletions autokuma/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::error::{Error, Result};
pub use kuma_client::util::ResultLogger;
use serde_json::json;
use std::collections::BTreeMap;

pub fn group_by_prefix<A, B, I>(v: I, delimiter: &str) -> BTreeMap<String, Vec<(String, String)>>
Expand Down Expand Up @@ -31,3 +33,36 @@ impl<T, E> ResultOrDie<T> for std::result::Result<T, E> {
}
}
}

pub trait FlattenValue {
fn flatten(&self) -> Result<Vec<(String, serde_json::Value)>>;
}

impl FlattenValue for serde_json::Value {
fn flatten(&self) -> Result<Vec<(String, serde_json::Value)>> {
let mut map = serde_json::Map::new();
insert_object(
&mut map,
None,
self.as_object()
.ok_or_else(|| Error::DeserializeError("Not an object".to_string()))?,
);
Ok(map.into_iter().collect())
}
}

fn insert_object(
base_json: &mut serde_json::Map<String, serde_json::Value>,
base_key: Option<&str>,
object: &serde_json::Map<String, serde_json::Value>,
) {
for (key, value) in object {
let new_key = base_key.map_or_else(|| key.clone(), |base_key| format!("{base_key}.{key}"));

if let Some(object) = value.as_object() {
insert_object(base_json, Some(&new_key), object);
} else {
base_json.insert(new_key.to_string(), json!(value));
}
}
}

0 comments on commit 766ddbb

Please sign in to comment.