-
Notifications
You must be signed in to change notification settings - Fork 405
feat: initial draft of custom metric tool and its systemd timer #1963
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
f8849ba
feat: initial draft of custom metric tool and systemd timer
nabdullindfinity 3c302a0
fix: refactoring and review comments
nabdullindfinity f0b707f
fix: buildifier and gazelle linters
nabdullindfinity 32f70c4
Merge branch 'master' into na/interrupts-collector
nabdullindfinity 91c0526
fix: cargo clippy warnings and cargo packaging
nabdullindfinity File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
ic-os/components/monitoring/custom-metrics/metrics_tool.service
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| [Unit] | ||
| Description=Report custom metrics once per minute | ||
|
|
||
| [Service] | ||
| Type=oneshot | ||
| ExecStart=/opt/ic/bin/metrics_tool --metrics /run/node_exporter/collector_textfile/custom_metrics.prom | ||
| DeviceAllow=/dev/vda | ||
| IPAddressDeny=any | ||
| LockPersonality=yes | ||
| MemoryDenyWriteExecute=yes | ||
| NoNewPrivileges=yes | ||
| PrivateDevices=no | ||
| PrivateNetwork=yes | ||
| PrivateTmp=yes | ||
| PrivateUsers=no | ||
| ProtectClock=yes | ||
| ProtectControlGroups=yes | ||
| ProtectHome=yes | ||
| ProtectHostname=yes | ||
| ProtectKernelModules=yes | ||
| ProtectKernelTunables=yes | ||
| ProtectSystem=strict | ||
| ReadOnlyPaths=/proc/interrupts | ||
| ReadWritePaths=/run/node_exporter/collector_textfile | ||
| RestrictAddressFamilies=AF_UNIX | ||
| RestrictAddressFamilies=~AF_UNIX | ||
| RestrictNamespaces=yes | ||
| RestrictRealtime=yes | ||
| RestrictSUIDSGID=yes | ||
| SystemCallArchitectures=native | ||
| SystemCallErrorNumber=EPERM | ||
| SystemCallFilter=@system-service | ||
| UMask=022 |
10 changes: 10 additions & 0 deletions
10
ic-os/components/monitoring/custom-metrics/metrics_tool.timer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| [Unit] | ||
| Description=Collect custom metrics every minute | ||
|
|
||
| [Timer] | ||
| OnBootSec=60s | ||
| OnUnitActiveSec=60s | ||
| Unit=metrics_tool.service | ||
|
|
||
| [Install] | ||
| WantedBy=timers.target |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test", "rust_test_suite") | ||
|
|
||
| package(default_visibility = ["//rs:ic-os-pkg"]) | ||
|
|
||
| DEPENDENCIES = [ | ||
| # Keep sorted. | ||
| "//rs/sys", | ||
| "@crate_index//:anyhow", | ||
| "@crate_index//:clap", | ||
| ] | ||
|
|
||
| DEV_DEPENDENCIES = [ | ||
| # Keep sorted. | ||
| ] | ||
|
|
||
| MACRO_DEPENDENCIES = [] | ||
|
|
||
| ALIASES = {} | ||
|
|
||
| rust_library( | ||
| name = "metrics_tool", | ||
| srcs = glob( | ||
| ["src/**/*.rs"], | ||
| exclude = ["src/main.rs"], | ||
| ), | ||
| aliases = ALIASES, | ||
| crate_name = "ic_metrics_tool", | ||
| proc_macro_deps = MACRO_DEPENDENCIES, | ||
| visibility = ["//rs:system-tests-pkg"], | ||
| deps = DEPENDENCIES, | ||
| ) | ||
|
|
||
| rust_binary( | ||
| name = "metrics_tool_bin", | ||
| srcs = ["src/main.rs"], | ||
| aliases = ALIASES, | ||
| proc_macro_deps = MACRO_DEPENDENCIES, | ||
| deps = DEPENDENCIES + [":metrics_tool"], | ||
| ) | ||
|
|
||
| rust_test( | ||
| name = "metrics_tool_test", | ||
| crate = ":metrics_tool", | ||
| deps = DEPENDENCIES + DEV_DEPENDENCIES, | ||
| ) | ||
|
|
||
| rust_test_suite( | ||
| name = "metrics_tool_integration", | ||
| srcs = glob(["tests/**/*.rs"]), | ||
| target_compatible_with = [ | ||
| "@platforms//os:linux", | ||
| ], | ||
| deps = [":metrics_tool_bin"] + DEPENDENCIES + DEV_DEPENDENCIES, | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| [package] | ||
| name = "ic-metrics-tool" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
|
|
||
| [[bin]] | ||
| name = "metrics_tool" | ||
| path = "src/main.rs" | ||
|
|
||
| [dependencies] | ||
| anyhow = { workspace = true } | ||
| clap = { workspace = true } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| // TODO: refactor/merge this with fstrim_tool and guestos_tool metrics functionality | ||
| use std::fs::File; | ||
| use std::io::{self, Write}; | ||
| use std::path::Path; | ||
|
|
||
| // TODO: everything is floating point for now | ||
| pub struct Metric { | ||
| name: String, | ||
| value: f64, | ||
| annotation: String, | ||
| labels: Vec<(String, String)>, | ||
| } | ||
|
|
||
| impl Metric { | ||
| pub fn new(name: &str, value: f64) -> Self { | ||
| Self { | ||
| name: name.to_string(), | ||
| value, | ||
| annotation: "Custom metric".to_string(), | ||
| labels: Vec::new(), | ||
| } | ||
| } | ||
| pub fn with_annotation(name: &str, value: f64, annotation: &str) -> Self { | ||
| Self { | ||
| name: name.to_string(), | ||
| value, | ||
| annotation: annotation.to_string(), | ||
| labels: Vec::new(), | ||
| } | ||
| } | ||
|
|
||
| pub fn add_annotation(mut self, annotation: &str) -> Self { | ||
| self.annotation = annotation.to_string(); | ||
| self | ||
| } | ||
|
|
||
| pub fn add_label(mut self, key: &str, value: &str) -> Self { | ||
| self.labels.push((key.to_string(), value.to_string())); | ||
| self | ||
| } | ||
|
|
||
| // TODO: formatting of floats | ||
| // Convert to prometheus exposition format | ||
| pub fn to_prom_string(&self) -> String { | ||
| let labels_str = if self.labels.is_empty() { | ||
| String::new() | ||
| } else { | ||
| let labels: Vec<String> = self | ||
| .labels | ||
| .iter() | ||
| .map(|(k, v)| format!("{}=\"{}\"", k, v)) | ||
| .collect(); | ||
| format!("{{{}}}", labels.join(",")) | ||
| }; | ||
| format!( | ||
| "# HELP {} {}\n\ | ||
| # TYPE {} counter\n\ | ||
| {}{} {}", | ||
| self.name, self.annotation, self.name, self.name, labels_str, self.value | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| pub struct MetricsWriter { | ||
| file_path: String, | ||
| } | ||
|
|
||
| impl MetricsWriter { | ||
| pub fn new(file_path: &str) -> Self { | ||
| Self { | ||
| file_path: file_path.to_string(), | ||
| } | ||
| } | ||
|
|
||
| pub fn write_metrics(&self, metrics: &[Metric]) -> io::Result<()> { | ||
| let path = Path::new(&self.file_path); | ||
| let mut file = File::create(path)?; | ||
| for metric in metrics { | ||
| writeln!(file, "{}", metric.to_prom_string())?; | ||
| } | ||
| Ok(()) | ||
| } | ||
| } | ||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn test_metric_to_string() { | ||
| let metric = Metric::new("test_metric", 123.45) | ||
| .add_label("label1", "value1") | ||
| .add_label("label2", "value2"); | ||
| assert_eq!( | ||
| metric.to_prom_string(), | ||
| "# HELP test_metric Custom metric\n\ | ||
| # TYPE test_metric counter\n\ | ||
| test_metric{label1=\"value1\",label2=\"value2\"} 123.45" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_write_metrics() { | ||
| let metrics = vec![ | ||
| Metric::new("metric1", 1.0), | ||
| Metric::new("metric2", 2.0).add_label("label", "value"), | ||
| ]; | ||
| let writer = MetricsWriter::new("/tmp/test_metrics.prom"); | ||
| writer.write_metrics(&metrics).unwrap(); | ||
| let content = std::fs::read_to_string("/tmp/test_metrics.prom").unwrap(); | ||
| assert!(content.contains( | ||
| "# HELP metric1 Custom metric\n\ | ||
| # TYPE metric1 counter\n\ | ||
| metric1 1" | ||
| )); | ||
| assert!(content.contains( | ||
| "# HELP metric2 Custom metric\n\ | ||
| # TYPE metric2 counter\n\ | ||
| metric2{label=\"value\"} 2" | ||
| )); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_metric_large_value() { | ||
| let metric = Metric::new("large_value_metric", 1.0e64); | ||
| assert_eq!( | ||
| metric.to_prom_string(), | ||
| "# HELP large_value_metric Custom metric\n\ | ||
| # TYPE large_value_metric counter\n\ | ||
| large_value_metric 10000000000000000000000000000000000000000000000000000000000000000" | ||
| ); | ||
| } | ||
|
|
||
|
nabdullindfinity marked this conversation as resolved.
|
||
| #[test] | ||
| fn test_metric_without_labels() { | ||
| let metric = Metric::new("no_label_metric", 42.0); | ||
| assert_eq!( | ||
| metric.to_prom_string(), | ||
| "# HELP no_label_metric Custom metric\n\ | ||
| # TYPE no_label_metric counter\n\ | ||
| no_label_metric 42" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_metric_with_annotation() { | ||
| let metric = Metric::with_annotation("annotated_metric", 99.9, "This is a test metric"); | ||
| assert_eq!( | ||
| metric.to_prom_string(), | ||
| "# HELP annotated_metric This is a test metric\n\ | ||
| # TYPE annotated_metric counter\n\ | ||
| annotated_metric 99.9" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_write_empty_metrics() { | ||
| let metrics: Vec<Metric> = Vec::new(); | ||
| let writer = MetricsWriter::new("/tmp/test_empty_metrics.prom"); | ||
| writer.write_metrics(&metrics).unwrap(); | ||
| let content = std::fs::read_to_string("/tmp/test_empty_metrics.prom").unwrap(); | ||
| assert!(content.is_empty()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_metric_with_multiple_labels() { | ||
| let metric = Metric::new("multi_label_metric", 10.0) | ||
| .add_label("foo", "bar") | ||
| .add_label("version", "1.0.0"); | ||
| assert_eq!( | ||
| metric.to_prom_string(), | ||
| "# HELP multi_label_metric Custom metric\n\ | ||
| # TYPE multi_label_metric counter\n\ | ||
| multi_label_metric{foo=\"bar\",version=\"1.0.0\"} 10" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_metric_with_empty_annotation() { | ||
| let metric = Metric::with_annotation("empty_annotation_metric", 5.5, ""); | ||
| assert_eq!( | ||
| metric.to_prom_string(), | ||
| "# HELP empty_annotation_metric \n\ | ||
| # TYPE empty_annotation_metric counter\n\ | ||
| empty_annotation_metric 5.5" | ||
| ); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.